fix and update
This commit is contained in:
@@ -160,7 +160,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
setHasStoredSession(true);
|
setHasStoredSession(true);
|
||||||
clearPinLock();
|
clearPinLock();
|
||||||
if (auth.pinVerified !== false) {
|
if (auth.pinVerified !== false) {
|
||||||
void syncFedcmSession(auth.accessToken);
|
window.setTimeout(() => {
|
||||||
|
void syncFedcmSession(auth.accessToken);
|
||||||
|
}, 300);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[clearPinLock]
|
[clearPinLock]
|
||||||
|
|||||||
@@ -506,7 +506,7 @@ export async function refreshAuthSession(): Promise<RefreshSessionResponse> {
|
|||||||
throw new ApiError('Refresh token отсутствует', 401, 'NO_REFRESH');
|
throw new ApiError('Refresh token отсутствует', 401, 'NO_REFRESH');
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${getApiUrl()}/auth/refresh`, {
|
const response = await fetchWithGatewayRetry(`${getApiUrl()}/auth/refresh`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ refreshToken, sessionId: sessionId ?? undefined })
|
body: JSON.stringify({ refreshToken, sessionId: sessionId ?? undefined })
|
||||||
@@ -522,7 +522,7 @@ export async function refreshAuthSession(): Promise<RefreshSessionResponse> {
|
|||||||
export async function syncFedcmSession(accessToken: string) {
|
export async function syncFedcmSession(accessToken: string) {
|
||||||
if (typeof window === 'undefined' || !accessToken.trim()) return;
|
if (typeof window === 'undefined' || !accessToken.trim()) return;
|
||||||
try {
|
try {
|
||||||
await fetch(`${getApiUrl()}/fedcm/session/sync`, {
|
await fetchWithGatewayRetry(`${getApiUrl()}/fedcm/session/sync`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { Authorization: `Bearer ${accessToken.trim()}` },
|
headers: { Authorization: `Bearer ${accessToken.trim()}` },
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
@@ -624,6 +624,36 @@ export function buildSystemSettingPayload(setting: Pick<SystemSetting, 'key' | '
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
||||||
|
const GATEWAY_RETRY_ATTEMPTS = 3;
|
||||||
|
|
||||||
|
function sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Повтор при кратковременном 502/503/504 (nginx не достучался до api-gateway после перезапуска). */
|
||||||
|
async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Response> {
|
||||||
|
let lastNetworkError: unknown;
|
||||||
|
for (let attempt = 0; attempt < GATEWAY_RETRY_ATTEMPTS; attempt++) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, init);
|
||||||
|
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||||
|
await sleep(250 * (attempt + 1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
lastNetworkError = error;
|
||||||
|
if (attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||||
|
await sleep(250 * (attempt + 1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw mapFetchError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw mapFetchError(lastNetworkError);
|
||||||
|
}
|
||||||
|
|
||||||
export async function apiFetch<T>(path: string, options: RequestInit = {}, token?: string | null, allowRetry = true): Promise<T> {
|
export async function apiFetch<T>(path: string, options: RequestInit = {}, token?: string | null, allowRetry = true): Promise<T> {
|
||||||
const resolvedToken = token ?? (typeof window !== 'undefined' ? window.localStorage.getItem(AUTH_TOKEN_KEY) : null);
|
const resolvedToken = token ?? (typeof window !== 'undefined' ? window.localStorage.getItem(AUTH_TOKEN_KEY) : null);
|
||||||
const method = (options.method ?? 'GET').toUpperCase();
|
const method = (options.method ?? 'GET').toUpperCase();
|
||||||
@@ -638,11 +668,14 @@ export async function apiFetch<T>(path: string, options: RequestInit = {}, token
|
|||||||
|
|
||||||
let response: Response;
|
let response: Response;
|
||||||
try {
|
try {
|
||||||
response = await fetch(`${getApiUrl()}${path}`, {
|
response = await fetchWithGatewayRetry(`${getApiUrl()}${path}`, {
|
||||||
...options,
|
...options,
|
||||||
headers
|
headers
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof ApiError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
throw mapFetchError(error);
|
throw mapFetchError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
install.sh
19
install.sh
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_VERSION="2.3.6"
|
SCRIPT_VERSION="2.3.7"
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
@@ -1912,16 +1912,16 @@ render_proxy_rewrite() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# proxy_pass для /idp-api/ — срезает префикс /idp-api/ и проксирует на корень.
|
# proxy_pass для /idp-api/ — named capture надёжнее rewrite+break с переменной upstream.
|
||||||
render_proxy_idp_api() {
|
render_proxy_idp_api() {
|
||||||
local key="api" up var
|
local key="api" up var
|
||||||
up="$(nginx_upstream "$key")"
|
up="$(nginx_upstream "$key")"
|
||||||
if [[ "$NGINX_MODE" == "docker" ]]; then
|
if [[ "$NGINX_MODE" == "docker" ]]; then
|
||||||
var="$(nginx_upstream_var "$key")"
|
var="$(nginx_upstream_var "$key")"
|
||||||
printf '%s\n set $%s %s;\n rewrite ^/idp-api/(.*)$ /$1 break;\n proxy_pass $%s;' \
|
printf '%s\n set $%s %s;\n proxy_pass $%s/$api_path$is_args$args;' \
|
||||||
"$NGINX_DOCKER_RESOLVER" "$var" "$up" "$var"
|
"$NGINX_DOCKER_RESOLVER" "$var" "$up" "$var"
|
||||||
else
|
else
|
||||||
printf ' proxy_pass %s/;' "$up"
|
printf ' proxy_pass %s/$api_path$is_args$args;' "$up"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2095,9 +2095,10 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_nginx_idp_api_proxy_block() {
|
build_nginx_idp_api_proxy_block() {
|
||||||
local proxy_ws proxy_idp proxy_oauth proxy_wk proxy_fedcm
|
local proxy_ws proxy_idp proxy_auth proxy_oauth proxy_wk proxy_fedcm
|
||||||
proxy_ws="$(render_proxy_rewrite ws /ws)"
|
proxy_ws="$(render_proxy_rewrite ws /ws)"
|
||||||
proxy_idp="$(render_proxy_idp_api)"
|
proxy_idp="$(render_proxy_idp_api)"
|
||||||
|
proxy_auth="$(render_proxy_root api)"
|
||||||
proxy_oauth="$(render_proxy_root api)"
|
proxy_oauth="$(render_proxy_root api)"
|
||||||
proxy_wk="$(render_proxy_root api)"
|
proxy_wk="$(render_proxy_root api)"
|
||||||
proxy_fedcm="$(render_proxy_root api)"
|
proxy_fedcm="$(render_proxy_root api)"
|
||||||
@@ -2114,12 +2115,18 @@ ${proxy_ws}
|
|||||||
proxy_read_timeout 86400s;
|
proxy_read_timeout 86400s;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /idp-api/ {
|
location ~ ^/idp-api/(?<api_path>.*)$ {
|
||||||
${proxy_idp}
|
${proxy_idp}
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
${nginx_proxy_headers}
|
${nginx_proxy_headers}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /auth/ {
|
||||||
|
${proxy_auth}
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
${nginx_proxy_headers}
|
||||||
|
}
|
||||||
|
|
||||||
location /oauth/ {
|
location /oauth/ {
|
||||||
${proxy_oauth}
|
${proxy_oauth}
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|||||||
Reference in New Issue
Block a user