fix and update

This commit is contained in:
lendry
2026-07-01 14:58:30 +03:00
parent 0c9b8e2629
commit 7d344fb82e
3 changed files with 72 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service'; import { CoreGrpcService } from '../core-grpc.service';
import { normalizePublicBaseUrl, pickBestPublicBase, isInternalHostname } from './public-url'; import { normalizePublicBaseUrl, pickBestPublicBase, isInternalHostname, normalizeDedicatedApiIssuer } from './public-url';
function normalizeBaseUrl(url: string) { function normalizeBaseUrl(url: string) {
return normalizePublicBaseUrl(url); return normalizePublicBaseUrl(url);
@@ -52,7 +52,8 @@ export async function resolveOAuthIssuer(core: CoreGrpcService, fallback = 'http
const fromDomain = await resolveProjectDomainUrl(core, true); const fromDomain = await resolveProjectDomainUrl(core, true);
// env первым: в Docker .env перекрывает seed localhost в БД после первого деплоя. // env первым: в Docker .env перекрывает seed localhost в БД после первого деплоя.
return pickBestPublicBase([envIssuer, fromDb, fromDomain], fallback); const picked = pickBestPublicBase([envIssuer, fromDb, fromDomain], fallback);
return normalizeDedicatedApiIssuer(picked);
} }
export async function resolveFrontendUrl(core: CoreGrpcService, fallback = 'http://localhost:3002'): Promise<string> { export async function resolveFrontendUrl(core: CoreGrpcService, fallback = 'http://localhost:3002'): Promise<string> {

View File

@@ -17,6 +17,25 @@ export function normalizePublicBaseUrl(url: string) {
return url.trim().replace(/\/+$/, ''); return url.trim().replace(/\/+$/, '');
} }
/**
* На выделенном API-домене (api.example.com) gateway слушает в корне (/fedcm, /oauth).
* Префикс /idp-api нужен только на SSO-домене. Убираем лишний суффикс из PUBLIC_API_URL.
*/
export function normalizeDedicatedApiIssuer(url: string) {
const normalized = normalizePublicBaseUrl(url);
try {
const parsed = new URL(normalized);
const host = parsed.hostname.toLowerCase();
const path = parsed.pathname.replace(/\/+$/, '') || '/';
if (host.startsWith('api.') && (path === '/idp-api' || path.endsWith('/idp-api'))) {
return `${parsed.protocol}//${parsed.host}`;
}
} catch {
return normalized;
}
return normalized;
}
export function isInternalHostname(hostname: string) { export function isInternalHostname(hostname: string) {
const host = hostname.trim().toLowerCase(); const host = hostname.trim().toLowerCase();
if (!host) { if (!host) {

View File

@@ -2312,9 +2312,32 @@ EOF
build_nginx_api_location_block() { build_nginx_api_location_block() {
if [[ "$NGINX_MODE" == "docker" ]]; then if [[ "$NGINX_MODE" == "docker" ]]; then
local proxy local proxy proxy_idp proxy_ws
proxy="$(render_proxy_root api)" proxy="$(render_proxy_root api)"
proxy_idp="$(render_proxy_idp_api)"
proxy_ws="$(render_proxy_idp_ws)"
cat <<EOF cat <<EOF
# Алиас /idp-api на API-домене: совместимость с PUBLIC_API_URL из документации и curl.
location ^~ /idp-api/ws {
${proxy_ws}
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 86400s;
proxy_connect_timeout 5s;
proxy_next_upstream error timeout http_502 http_503 http_504;
proxy_next_upstream_tries 3;
}
location ^~ /idp-api/ {
${proxy_idp}
${nginx_proxy_headers_api}
}
location / { location / {
${proxy} ${proxy}
${nginx_proxy_headers_api} ${nginx_proxy_headers_api}
@@ -3031,6 +3054,25 @@ verify_fedcm_endpoints() {
echo -e " ${YELLOW}!${NC} FedCM discover.json → HTTP ${code} (${api_domain}, ожидалось 200)" echo -e " ${YELLOW}!${NC} FedCM discover.json → HTTP ${code} (${api_domain}, ожидалось 200)"
fi fi
local config_login_url config_login_url_alias
config_login_url="$(curl ${curl_extra} -fsS --max-time 8 \
-H "Sec-Fetch-Dest: webidentity" \
"${scheme}://${api_domain}/fedcm/config.json" 2>/dev/null \
| jq -r '.login_url // empty' 2>/dev/null || true)"
config_login_url_alias="$(curl ${curl_extra} -fsS --max-time 8 \
-H "Sec-Fetch-Dest: webidentity" \
"${scheme}://${api_domain}/idp-api/fedcm/config.json" 2>/dev/null \
| jq -r '.login_url // empty' 2>/dev/null || true)"
if [[ -n "$config_login_url" ]]; then
echo -e " ${GREEN}${NC} FedCM config.json login_url=${config_login_url}"
elif [[ -n "$config_login_url_alias" ]]; then
echo -e " ${YELLOW}!${NC} FedCM config.json: используйте ${scheme}://${api_domain}/fedcm/config.json (не /idp-api/ на API-домене)"
echo -e " ${GREEN}${NC} FedCM config.json (alias /idp-api) login_url=${config_login_url_alias}"
else
echo -e " ${RED}${NC} FedCM config.json без login_url (${api_domain}) — проверьте PUBLIC_FRONTEND_URL и api-gateway"
return 1
fi
apex_domain="$(registrable_domain "${frontend_domain:-$api_domain}")" apex_domain="$(registrable_domain "${frontend_domain:-$api_domain}")"
if [[ -z "$apex_domain" \ if [[ -z "$apex_domain" \
|| "$apex_domain" == "$frontend_domain" \ || "$apex_domain" == "$frontend_domain" \
@@ -3043,6 +3085,13 @@ verify_fedcm_endpoints() {
body="$(curl ${curl_extra} -fsS --max-time 8 "${scheme}://${apex_domain}/.well-known/web-identity" 2>/dev/null || true)" body="$(curl ${curl_extra} -fsS --max-time 8 "${scheme}://${apex_domain}/.well-known/web-identity" 2>/dev/null || true)"
if [[ "$code" == "200" && "$body" == *provider_urls* ]]; then if [[ "$code" == "200" && "$body" == *provider_urls* ]]; then
echo -e " ${GREEN}${NC} FedCM web-identity → 200 (${apex_domain})" echo -e " ${GREEN}${NC} FedCM web-identity → 200 (${apex_domain})"
local well_known_login config_login
well_known_login="$(printf '%s' "$body" | jq -r '.login_url // empty' 2>/dev/null || true)"
config_login="${config_login_url:-$config_login_url_alias}"
if [[ -n "$well_known_login" && -n "$config_login" && "$well_known_login" != "$config_login" ]]; then
echo -e " ${RED}${NC} FedCM login_url не совпадает: web-identity=${well_known_login}, config.json=${config_login}"
return 1
fi
return 0 return 0
elif [[ "$code" == "000" ]]; then elif [[ "$code" == "000" ]]; then
echo -e " ${RED}${NC} FedCM web-identity недоступен (${apex_domain}) — ERR_CERT или DNS" echo -e " ${RED}${NC} FedCM web-identity недоступен (${apex_domain}) — ERR_CERT или DNS"