fix and update
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -523,8 +523,6 @@ export async function refreshAuthSession(): Promise<RefreshSessionResponse> {
|
||||
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<SystemSetting, 'key' | '
|
||||
}
|
||||
|
||||
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
||||
const GATEWAY_RETRY_ATTEMPTS = 4;
|
||||
const GATEWAY_RETRY_ATTEMPTS = 6;
|
||||
const GATEWAY_STABLE_PROBE_INTERVAL_MS = 800;
|
||||
const GATEWAY_STABLE_MAX_ROUNDS = 8;
|
||||
const API_MAX_CONCURRENT = 6;
|
||||
@@ -872,10 +870,6 @@ async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Re
|
||||
}
|
||||
|
||||
export async function apiFetch<T>(path: string, options: RequestInit = {}, token?: string | null, allowRetry = true): Promise<T> {
|
||||
if (typeof window !== 'undefined' && !gatewayReadyResolved && !path.startsWith('/health')) {
|
||||
await ensureApiGatewayReady();
|
||||
}
|
||||
|
||||
await acquireApiSlot();
|
||||
|
||||
const resolvedToken = resolveAccessToken(token);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] $*"
|
||||
|
||||
52
install.sh
52
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))
|
||||
|
||||
Reference in New Issue
Block a user