From 2ea790d21d10fac38e2a388cacd76235b6201895 Mon Sep 17 00:00:00 2001 From: lendry Date: Tue, 30 Jun 2026 17:57:09 +0300 Subject: [PATCH] fix and update --- apps/frontend/components/id/auth-provider.tsx | 48 +++++++++++++---- apps/frontend/lib/api.ts | 8 +-- docker-compose.yml | 4 +- infra/nginx/99-idp-gateway-watch.sh | 2 +- install.sh | 52 +++++++++---------- 5 files changed, 68 insertions(+), 46 deletions(-) diff --git a/apps/frontend/components/id/auth-provider.tsx b/apps/frontend/components/id/auth-provider.tsx index 42151fc..86259f6 100644 --- a/apps/frontend/components/id/auth-provider.tsx +++ b/apps/frontend/components/id/auth-provider.tsx @@ -29,7 +29,6 @@ import { setPinRequiredHandler, subscribeApiReady, subscribeApiNotReady, - waitForStableGateway, } from '@/lib/api'; import { useToast } from './toast-provider'; import { PinLockModal } from './pin-lock-modal'; @@ -200,7 +199,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setIsApiReady(false); try { - await waitForStableGateway(1); const session = await fetchAuthSession(accessToken); applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock }); initialBootstrapDoneRef.current = true; @@ -230,14 +228,32 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { cacheUserProfile(auth.user); setHasStoredSession(true); clearPinLock(); + setBootstrapError(null); + initialBootstrapDoneRef.current = true; + setIsApiReady(true); + setIsSessionReady(true); + setIsLoading(false); - await warmUpAndApplySession(auth.accessToken); + void fetchAuthSession(auth.accessToken) + .then((session) => { + applySessionState(session, { setUser, setToken, activatePinLock, clearPinLock }); + }) + .catch((error) => { + if (isPinRequiredError(error)) { + activatePinLock(error.sessionId ?? auth.sessionId); + return; + } + if (!isGatewayUnavailableError(error)) { + const message = getApiErrorMessage(error, 'Не удалось обновить профиль после входа'); + if (message) showToast(message); + } + }); if (auth.pinVerified !== false) { scheduleFedcmSessionSync(auth.accessToken, 8000); } }, - [clearPinLock, warmUpAndApplySession] + [activatePinLock, clearPinLock, showToast] ); const logout = React.useCallback(() => { @@ -286,10 +302,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { try { for (let bootstrapAttempt = 0; bootstrapAttempt < 4; bootstrapAttempt += 1) { try { - if (isInitialBootstrap) { - await waitForStableGateway(1); - } - if (currentToken) { setToken(currentToken); try { @@ -580,13 +592,29 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { window.localStorage.setItem(AUTH_SESSION_KEY, sessionId); setToken(response.accessToken); - await warmUpAndApplySession(response.accessToken); + try { + await warmUpAndApplySession(response.accessToken); + } catch (error) { + if (!isGatewayUnavailableError(error)) { + throw error; + } + const cachedUser = readCachedUserProfile(); + if (cachedUser) { + setUser(cachedUser); + } + clearPinLock(); + setBootstrapError(null); + initialBootstrapDoneRef.current = true; + setIsApiReady(true); + setIsSessionReady(true); + setIsLoading(false); + } if (pathname.startsWith('/auth/')) { router.replace('/'); } }, - [pathname, router, warmUpAndApplySession] + [clearPinLock, pathname, router, warmUpAndApplySession] ); const handlePinUnlock = React.useCallback( diff --git a/apps/frontend/lib/api.ts b/apps/frontend/lib/api.ts index ba0054c..f1446ca 100644 --- a/apps/frontend/lib/api.ts +++ b/apps/frontend/lib/api.ts @@ -523,8 +523,6 @@ export async function refreshAuthSession(): Promise { throw new ApiError('Refresh token недоступен на сервере', 401, 'NO_REFRESH'); } - await ensureApiGatewayReady(); - const refreshToken = window.localStorage.getItem(AUTH_REFRESH_KEY); const sessionId = window.localStorage.getItem(AUTH_SESSION_KEY); if (!refreshToken) { @@ -682,7 +680,7 @@ export function buildSystemSettingPayload(setting: Pick(path: string, options: RequestInit = {}, token?: string | null, allowRetry = true): Promise { - if (typeof window !== 'undefined' && !gatewayReadyResolved && !path.startsWith('/health')) { - await ensureApiGatewayReady(); - } - await acquireApiSlot(); const resolvedToken = resolveAccessToken(token); diff --git a/docker-compose.yml b/docker-compose.yml index 83fa4f3..ee88bed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -268,7 +268,7 @@ services: container_name: lendry-id-nginx restart: unless-stopped environment: - IDP_API_GATEWAY_HOST: lendry-id-api-gateway + IDP_API_GATEWAY_HOST: api-gateway IDP_API_GATEWAY_PORT: "3000" IDP_FRONTEND_DOMAIN: ${DOMAIN_FRONTEND:-} ports: @@ -291,7 +291,7 @@ services: test: [ "CMD-SHELL", - "if [ -n \"$IDP_FRONTEND_DOMAIN\" ]; then wget -qO- --timeout=4 --no-check-certificate --header=\"Host: $IDP_FRONTEND_DOMAIN\" \"https://127.0.0.1/idp-api/health\" 2>/dev/null | grep -q '\"status\"' || wget -qO- --timeout=4 --header=\"Host: $IDP_FRONTEND_DOMAIN\" \"http://127.0.0.1/idp-api/health\" 2>/dev/null | grep -q '\"status\"' || exit 1; else wget -qO- --timeout=4 http://lendry-id-api-gateway:3000/health 2>/dev/null | grep -q '\"status\"' || exit 1; fi", + "if [ -n \"$IDP_FRONTEND_DOMAIN\" ]; then wget -qO- --timeout=4 --no-check-certificate --header=\"Host: $IDP_FRONTEND_DOMAIN\" \"https://127.0.0.1/idp-api/health\" 2>/dev/null | grep -q '\"status\"' || wget -qO- --timeout=4 --header=\"Host: $IDP_FRONTEND_DOMAIN\" \"http://127.0.0.1/idp-api/health\" 2>/dev/null | grep -q '\"status\"' || exit 1; else wget -qO- --timeout=4 http://api-gateway:3000/health 2>/dev/null | grep -q '\"status\"' || exit 1; fi", ] interval: 15s timeout: 8s diff --git a/infra/nginx/99-idp-gateway-watch.sh b/infra/nginx/99-idp-gateway-watch.sh index e33ae00..f856320 100644 --- a/infra/nginx/99-idp-gateway-watch.sh +++ b/infra/nginx/99-idp-gateway-watch.sh @@ -4,7 +4,7 @@ set -eu WATCH_INTERVAL="${IDP_NGINX_WATCH_INTERVAL:-5}" -WATCH_HOSTS="${IDP_NGINX_WATCH_HOSTS:-lendry-id-api-gateway:3000/health lendry-id-frontend:3000/}" +WATCH_HOSTS="${IDP_NGINX_WATCH_HOSTS:-api-gateway:3000/health frontend:3000/}" log() { echo "[idp-nginx-watch] $*" diff --git a/install.sh b/install.sh index 91817ec..ef27b9e 100644 --- a/install.sh +++ b/install.sh @@ -1705,19 +1705,19 @@ nginx_upstream() { case "$1" in api) if [[ "$NGINX_MODE" == "docker" ]]; then - echo "http://lendry-id-api-gateway:3000" + echo "http://api-gateway:3000" else echo "http://127.0.0.1:3000" fi ;; frontend) - [[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-frontend:3000" || echo "http://127.0.0.1:3002" + [[ "$NGINX_MODE" == "docker" ]] && echo "http://frontend:3000" || echo "http://127.0.0.1:3002" ;; docs) - [[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-docs:3000" || echo "http://127.0.0.1:3003" + [[ "$NGINX_MODE" == "docker" ]] && echo "http://docs:3000" || echo "http://127.0.0.1:3003" ;; ws) - [[ "$NGINX_MODE" == "docker" ]] && echo "http://lendry-id-media-ws:8085" || echo "http://127.0.0.1:8085" + [[ "$NGINX_MODE" == "docker" ]] && echo "http://media-ws:8085" || echo "http://127.0.0.1:8085" ;; minio) [[ "$NGINX_MODE" == "docker" ]] && echo "http://minio:9000" || echo "http://127.0.0.1:9000" @@ -2684,13 +2684,13 @@ validate_docker_api_nginx_config() { fail "Конфиг ${conf} содержит legacy upstream{} — выполните ./install.sh --fix-all" fi - 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" + if ! grep -q 'api-gateway:3000' "$conf" 2>/dev/null; then + fail "Конфиг ${conf} без proxy_pass на api-gateway:3000 — выполните ./install.sh --fix-all" fi if grep -q 'proxy_pass.*\$request_uri' "$conf" 2>/dev/null; then fail "Конфиг ${conf} содержит proxy_pass \$request_uri — нужен ./install.sh --fix-all (502 на / без resolver)" fi - ok "Конфиг API-домена: proxy_pass → lendry-id-api-gateway:3000" + ok "Конфиг API-домена: proxy_pass → api-gateway:3000" } validate_docker_frontend_nginx_config() { @@ -2702,13 +2702,13 @@ validate_docker_frontend_nginx_config() { fail "Конфиг ${conf} без location /idp-api — браузер SPA получит 502. Выполните ./install.sh --fix-all" fi - 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" + if ! grep -q 'api-gateway:3000' "$conf" 2>/dev/null; then + fail "Конфиг ${conf} без proxy_pass на api-gateway:3000 — выполните ./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" + ok "Конфиг SSO-домена: /idp-api → api-gateway:3000" } validate_all_docker_nginx_proxy_pass() { @@ -2735,9 +2735,9 @@ verify_nginx_reaches_api_gateway() { while [[ $attempt -lt $max_attempts ]]; do gw_ip="$(docker_cmd inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lendry-id-api-gateway 2>/dev/null || true)" body="$(docker_cmd "${COMPOSE_STACK_CMD[@]}" exec -T nginx \ - wget -qO- --timeout=5 http://lendry-id-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 - ok "Docker Nginx → lendry-id-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 return 0 fi @@ -2747,7 +2747,7 @@ verify_nginx_reaches_api_gateway() { fi done - warn "Docker Nginx не достучался до lendry-id-api-gateway:3000/health (IP: ${gw_ip:-?}, попыток: ${max_attempts})" + warn "Docker Nginx не достучался до api-gateway:3000/health (IP: ${gw_ip:-?}, попыток: ${max_attempts})" warn "Проверьте: docker compose --profile proxy ps api-gateway nginx && docker compose logs --tail=40 api-gateway nginx" return 1 } @@ -2806,9 +2806,9 @@ verify_nginx_reaches_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)" + wget -qO- --timeout=6 http://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:-?})" + ok "Docker Nginx → frontend:3000/ OK (IP frontend: ${fe_ip:-?})" if [[ -n "$frontend_domain" ]]; then verify_nginx_frontend_root_location "$frontend_domain" || return 1 fi @@ -2820,7 +2820,7 @@ verify_nginx_reaches_frontend() { fi done - warn "Docker Nginx не достучался до lendry-id-frontend:3000/ (IP: ${fe_ip:-?}, попыток: ${max_attempts})" + warn "Docker Nginx не достучался до frontend:3000/ (IP: ${fe_ip:-?}, попыток: ${max_attempts})" warn "Проверьте: docker compose --env-file .env --profile proxy logs --tail=40 frontend nginx" return 1 } @@ -2873,7 +2873,7 @@ check_sso_frontend_root() { 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 exec nginx wget -qO- http://frontend:3000/" echo " Решение: docker compose --env-file .env --profile proxy restart nginx frontend" return 1 else @@ -2887,12 +2887,12 @@ verify_nginx_reaches_media_ws() { 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)" + wget -qO- --timeout=5 http://media-ws:8085/health 2>/dev/null || true)" if [[ "$body" == *ok* ]] || [[ "$body" == *healthy* ]]; then - ok "Docker Nginx → lendry-id-media-ws:8085/health OK" + ok "Docker Nginx → media-ws:8085/health OK" return 0 fi - warn "Docker Nginx не достучался до lendry-id-media-ws:8085" + warn "Docker Nginx не достучался до media-ws:8085" warn "Проверьте: docker compose ps media-ws && docker compose logs --tail=40 media-ws" return 1 } @@ -2906,12 +2906,12 @@ check_sso_idp_api_proxy() { 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)" + wget -qO- --timeout=5 http://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" + echo " Проверьте: docker compose exec frontend wget -qO- http://api-gateway:3000/health" return 1 fi fi @@ -2928,7 +2928,7 @@ check_sso_idp_api_proxy() { 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" + echo " docker compose --profile proxy exec nginx wget -qO- http://api-gateway:3000/health" return 1 else echo -e " ${YELLOW}!${NC} SSO /idp-api/health → HTTP ${code} (ожидалось 200)" @@ -3353,7 +3353,7 @@ check_https_api_with_origin() { elif [[ "$code" == "502" ]]; then echo -e " ${RED}✘${NC} HTTPS API preflight (OPTIONS) → 502 — Nginx не достучался до api-gateway" echo " Проверьте: grep resolver nginx/conf.d/lendry-id-api.conf" - echo " docker compose --profile proxy exec nginx wget -qO- http://lendry-id-api-gateway:3000/health" + echo " docker compose --profile proxy exec nginx wget -qO- http://api-gateway:3000/health" return 1 else echo -e " ${YELLOW}!${NC} HTTPS API preflight (OPTIONS) → HTTP ${code} (ожидалось 204)" @@ -3435,8 +3435,8 @@ diagnose_common_issues() { elif grep -q '172\.28\.50\.10:3000' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" 2>/dev/null; then echo -e " ${RED}✘${NC} Устаревший конфиг (статический IP 172.28.50.10) — выполните ./install.sh --fix-all" issues=$((issues + 1)) - elif grep -q 'lendry-id-api-gateway:3000' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" 2>/dev/null; then - echo -e " ${GREEN}✔${NC} Nginx /idp-api: proxy_pass → lendry-id-api-gateway:3000" + elif grep -q 'api-gateway:3000' "${LOCAL_CONF_DIR}/${NGINX_PREFIX}-frontend.conf" 2>/dev/null; then + echo -e " ${GREEN}✔${NC} Nginx /idp-api: proxy_pass → api-gateway:3000" else echo -e " ${YELLOW}!${NC} Конфиг SSO без /idp-api proxy — выполните ./install.sh --fix-all" issues=$((issues + 1))