fix and update
This commit is contained in:
@@ -2,32 +2,57 @@ const API_ORIGIN_PROXY_PREFIX = '/idp-api';
|
|||||||
const configuredApiUrl = (process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000').replace(/\/$/, '');
|
const configuredApiUrl = (process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000').replace(/\/$/, '');
|
||||||
const configuredWsUrl = (process.env.NEXT_PUBLIC_WS_URL ?? 'ws://localhost:8085/ws').replace(/\/$/, '');
|
const configuredWsUrl = (process.env.NEXT_PUBLIC_WS_URL ?? 'ws://localhost:8085/ws').replace(/\/$/, '');
|
||||||
|
|
||||||
function resolveBrowserApiBaseUrl(): string {
|
function getServerApiUrl(): string {
|
||||||
// На сервере (SSR) обращаемся к API напрямую по настроенному URL.
|
return (process.env.INTERNAL_API_URL ?? configuredApiUrl).replace(/\/$/, '');
|
||||||
if (typeof window === 'undefined') {
|
}
|
||||||
return configuredApiUrl;
|
|
||||||
|
/** Same-origin /idp-api только если PUBLIC API URL явно задан как прокси-путь на фронтенде. */
|
||||||
|
function usesSameOriginApiProxy(): boolean {
|
||||||
|
if (configuredApiUrl.startsWith('/')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return /\/idp-api\/?$/i.test(configuredApiUrl) || configuredApiUrl.includes('/idp-api/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function usesSameOriginWsProxy(): boolean {
|
||||||
|
if (configuredWsUrl.startsWith('/')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return configuredWsUrl.includes('/idp-api/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveBrowserApiBaseUrl(): string {
|
||||||
|
// SSR: внутренний URL Docker-сети (api-gateway), не публичный домен.
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return getServerApiUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если URL уже относительный — используем как есть.
|
|
||||||
if (configuredApiUrl.startsWith('/')) {
|
if (configuredApiUrl.startsWith('/')) {
|
||||||
return configuredApiUrl.replace(/\/+$/, '') || API_ORIGIN_PROXY_PREFIX;
|
return configuredApiUrl.replace(/\/+$/, '') || API_ORIGIN_PROXY_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// В браузере ВСЕГДА ходим через same-origin прокси /idp-api (Next.js rewrites
|
// Split-domain (https://api.example.com): браузер ходит напрямую на API-домен.
|
||||||
// проксируют на внутренний api-gateway). Это исключает CORS и работу через
|
// Same-origin (https://sso.example.com/idp-api): через /idp-api и host/nginx rewrites.
|
||||||
// отдельный домен API (например api.idpmvk.lpr) для запросов самого UI IdP.
|
if (usesSameOriginApiProxy()) {
|
||||||
return API_ORIGIN_PROXY_PREFIX;
|
return API_ORIGIN_PROXY_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return configuredApiUrl;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveBrowserWsBaseUrl(): string {
|
function resolveBrowserWsBaseUrl(): string {
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return configuredWsUrl;
|
return configuredWsUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (usesSameOriginWsProxy()) {
|
||||||
const scheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
const scheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
return `${scheme}//${window.location.host}${API_ORIGIN_PROXY_PREFIX}/ws`;
|
return `${scheme}//${window.location.host}${API_ORIGIN_PROXY_PREFIX}/ws`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return configuredWsUrl;
|
||||||
|
}
|
||||||
|
|
||||||
export function getApiUrl(): string {
|
export function getApiUrl(): string {
|
||||||
return resolveBrowserApiBaseUrl();
|
return resolveBrowserApiBaseUrl();
|
||||||
}
|
}
|
||||||
@@ -36,7 +61,7 @@ export function getWsUrl(): string {
|
|||||||
return resolveBrowserWsBaseUrl();
|
return resolveBrowserWsBaseUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated Используйте getApiUrl() — URL может переключаться на same-origin /idp-api в браузере */
|
/** @deprecated Используйте getApiUrl() — в браузере URL зависит от режима (split-domain или /idp-api) */
|
||||||
export const API_URL = configuredApiUrl;
|
export const API_URL = configuredApiUrl;
|
||||||
|
|
||||||
export interface PublicUser {
|
export interface PublicUser {
|
||||||
|
|||||||
73
install.sh
73
install.sh
@@ -830,8 +830,10 @@ derive_public_urls() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$ssl_type" == "selfsigned" && -n "${DOMAIN_FRONTEND:-}" ]]; then
|
if [[ "$ssl_type" == "selfsigned" && -n "${DOMAIN_FRONTEND:-}" ]]; then
|
||||||
|
if [[ -z "${DOMAIN_API:-}" || "${DOMAIN_API}" == "${DOMAIN_FRONTEND}" ]]; then
|
||||||
apply_same_origin_api_proxy_urls "$scheme"
|
apply_same_origin_api_proxy_urls "$scheme"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
SSL_TYPE="$ssl_type"
|
SSL_TYPE="$ssl_type"
|
||||||
USE_NGINX_SSL=$([[ "$ssl_type" == "none" ]] && echo false || echo true)
|
USE_NGINX_SSL=$([[ "$ssl_type" == "none" ]] && echo false || echo true)
|
||||||
@@ -859,6 +861,44 @@ apply_same_origin_api_proxy_urls() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Split-domain (api.xxx + sso.xxx): PUBLIC_API_URL должен быть https://api.xxx,
|
||||||
|
# а не https://sso.xxx/idp-api — иначе фронтенд шлёт запросы не туда.
|
||||||
|
ensure_split_domain_public_urls() {
|
||||||
|
load_env
|
||||||
|
local api_domain frontend_domain public_api ssl_type scheme ws_url
|
||||||
|
api_domain="$(env_get DOMAIN_API "")"
|
||||||
|
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||||
|
public_api="$(env_get PUBLIC_API_URL "")"
|
||||||
|
ssl_type="$(env_get SSL_TYPE none)"
|
||||||
|
|
||||||
|
[[ -n "$api_domain" && -n "$frontend_domain" ]] || return 0
|
||||||
|
[[ "$api_domain" != "$frontend_domain" ]] || return 0
|
||||||
|
|
||||||
|
if [[ "$public_api" != *"/idp-api"* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
scheme="http"
|
||||||
|
[[ "$ssl_type" == "letsencrypt" || "$ssl_type" == "selfsigned" || "$ssl_type" == "custom" ]] && scheme="https"
|
||||||
|
|
||||||
|
warn "PUBLIC_API_URL=${public_api} — некорректно для split-domain (API и SSO на разных доменах)"
|
||||||
|
PUBLIC_API_URL="${scheme}://${api_domain}"
|
||||||
|
env_set PUBLIC_API_URL "${PUBLIC_API_URL}"
|
||||||
|
|
||||||
|
ws_url="$(env_get PUBLIC_WS_URL "")"
|
||||||
|
if [[ -z "$ws_url" || "$ws_url" == *"/idp-api"* ]]; then
|
||||||
|
if [[ "$scheme" == "https" ]]; then
|
||||||
|
PUBLIC_WS_URL="wss://${api_domain}/ws"
|
||||||
|
else
|
||||||
|
PUBLIC_WS_URL="ws://${api_domain}/ws"
|
||||||
|
fi
|
||||||
|
env_set PUBLIC_WS_URL "${PUBLIC_WS_URL}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
env_set INTERNAL_API_URL "http://api-gateway:3000"
|
||||||
|
ok "Исправлено: PUBLIC_API_URL=${PUBLIC_API_URL}, PUBLIC_WS_URL=$(env_get PUBLIC_WS_URL "")"
|
||||||
|
}
|
||||||
|
|
||||||
choose_ssl_type_interactive() {
|
choose_ssl_type_interactive() {
|
||||||
local default="${1:-none}"
|
local default="${1:-none}"
|
||||||
|
|
||||||
@@ -2597,7 +2637,6 @@ diagnose_common_issues() {
|
|||||||
if [[ "$(env_get NGINX_MODE docker)" == "host" ]] \
|
if [[ "$(env_get NGINX_MODE docker)" == "host" ]] \
|
||||||
&& [[ "$(env_get INSTALL_MODE local)" != "local" ]] \
|
&& [[ "$(env_get INSTALL_MODE local)" != "local" ]] \
|
||||||
&& [[ -n "$(env_get DOMAIN_API "")" ]]; then
|
&& [[ -n "$(env_get DOMAIN_API "")" ]]; then
|
||||||
local port
|
|
||||||
for port in 3000 3002 3003; do
|
for port in 3000 3002 3003; do
|
||||||
if ! port_in_use "$port"; then
|
if ! port_in_use "$port"; then
|
||||||
echo -e " ${RED}✘${NC} Порт 127.0.0.1:${port} не слушается — host Nginx получит 502"
|
echo -e " ${RED}✘${NC} Порт 127.0.0.1:${port} не слушается — host Nginx получит 502"
|
||||||
@@ -2626,6 +2665,23 @@ diagnose_common_issues() {
|
|||||||
echo -e " ${GREEN}✔${NC} api-gateway: контейнер запущен"
|
echo -e " ${GREEN}✔${NC} api-gateway: контейнер запущен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
local api_domain frontend_domain public_api
|
||||||
|
api_domain="$(env_get DOMAIN_API "")"
|
||||||
|
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||||
|
public_api="$(env_get PUBLIC_API_URL "")"
|
||||||
|
if [[ -n "$api_domain" && -n "$frontend_domain" && "$api_domain" != "$frontend_domain" ]]; then
|
||||||
|
if [[ "$public_api" == *"/idp-api"* ]]; then
|
||||||
|
echo -e " ${RED}✘${NC} Split-domain: PUBLIC_API_URL=${public_api} — должно быть https://${api_domain} (не /idp-api на SSO)"
|
||||||
|
echo " Решение: ./install.sh --fix-all"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
elif [[ "$public_api" == *"${frontend_domain}"* && "$public_api" != *"${api_domain}"* ]]; then
|
||||||
|
echo -e " ${RED}✘${NC} PUBLIC_API_URL указывает на домен SSO (${frontend_domain}), а не API (${api_domain})"
|
||||||
|
issues=$((issues + 1))
|
||||||
|
else
|
||||||
|
echo -e " ${GREEN}✔${NC} Split-domain: PUBLIC_API_URL=${public_api}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
if [[ "$issues" -eq 0 ]]; then
|
if [[ "$issues" -eq 0 ]]; then
|
||||||
ok "Критических проблем не обнаружено"
|
ok "Критических проблем не обнаружено"
|
||||||
@@ -2668,6 +2724,8 @@ show_status() {
|
|||||||
echo " DOMAIN_FRONTEND: $(env_get DOMAIN_FRONTEND -)"
|
echo " DOMAIN_FRONTEND: $(env_get DOMAIN_FRONTEND -)"
|
||||||
echo " DOMAIN_DOCS: $(env_get DOMAIN_DOCS -)"
|
echo " DOMAIN_DOCS: $(env_get DOMAIN_DOCS -)"
|
||||||
echo " DOMAIN_WS: $(env_get DOMAIN_WS -)"
|
echo " DOMAIN_WS: $(env_get DOMAIN_WS -)"
|
||||||
|
echo " PUBLIC_API_URL: $(env_get PUBLIC_API_URL -)"
|
||||||
|
echo " PUBLIC_WS_URL: $(env_get PUBLIC_WS_URL -)"
|
||||||
echo ""
|
echo ""
|
||||||
docker_cmd compose ps 2>/dev/null || warn "Контейнеры не запущены"
|
docker_cmd compose ps 2>/dev/null || warn "Контейнеры не запущены"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -2833,6 +2891,8 @@ action_fix_host_proxy() {
|
|||||||
log "Шаг 2/5: compose override (LDAP / Nginx)..."
|
log "Шаг 2/5: compose override (LDAP / Nginx)..."
|
||||||
write_compose_override
|
write_compose_override
|
||||||
|
|
||||||
|
ensure_split_domain_public_urls
|
||||||
|
|
||||||
log "Шаг 3/5: пересоздание api-gateway, frontend, docs, media-ws..."
|
log "Шаг 3/5: пересоздание api-gateway, frontend, docs, media-ws..."
|
||||||
compose_build_and_up "${HOST_EDGE_SERVICES[@]}"
|
compose_build_and_up "${HOST_EDGE_SERVICES[@]}"
|
||||||
ensure_host_upstreams
|
ensure_host_upstreams
|
||||||
@@ -2869,6 +2929,7 @@ action_fix_all_errors() {
|
|||||||
resolve_nginx_mode_and_ports
|
resolve_nginx_mode_and_ports
|
||||||
ensure_docker_nginx_not_conflicting
|
ensure_docker_nginx_not_conflicting
|
||||||
write_compose_override
|
write_compose_override
|
||||||
|
ensure_split_domain_public_urls
|
||||||
|
|
||||||
log "Шаг 3/6: пересборка api-gateway и sso-core..."
|
log "Шаг 3/6: пересборка api-gateway и sso-core..."
|
||||||
compose_build_and_up api-gateway sso-core
|
compose_build_and_up api-gateway sso-core
|
||||||
@@ -2920,7 +2981,15 @@ action_fix_all_errors() {
|
|||||||
|
|
||||||
ok "Исправление завершено"
|
ok "Исправление завершено"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Проверьте в браузере страницу после авторизации — запросы к /idp-api/* должны отвечать 200."
|
api_domain="$(env_get DOMAIN_API "")"
|
||||||
|
frontend_domain="$(env_get DOMAIN_FRONTEND "")"
|
||||||
|
if [[ -n "$api_domain" && -n "$frontend_domain" && "$api_domain" != "$frontend_domain" ]]; then
|
||||||
|
echo " Split-domain: в DevTools → Network запросы должны идти на https://${api_domain}/...,"
|
||||||
|
echo " а не на https://${frontend_domain}/idp-api/..."
|
||||||
|
echo " После пересборки frontend обновите страницу с полным сбросом кеша (Ctrl+Shift+R)."
|
||||||
|
else
|
||||||
|
echo " Проверьте в браузере — запросы к /idp-api/* должны отвечать 200."
|
||||||
|
fi
|
||||||
echo " Если ошибки остались: docker compose logs --tail=80 api-gateway"
|
echo " Если ошибки остались: docker compose logs --tail=80 api-gateway"
|
||||||
echo ""
|
echo ""
|
||||||
show_status
|
show_status
|
||||||
|
|||||||
Reference in New Issue
Block a user