fix(invites): logging detallado cuando Hub invite creation falla

Antes los fallos del Hub login y invite creation eran silenciosos o con
warning genérico. Ahora loguea status code, body y nombre de campo para
facilitar diagnóstico en producción.

- Detecta HUB_ADMIN_EMAIL/PASSWORD vacíos con error explícito
- Loguea status+body cuando login falla (401/403)
- Loguea status+body+tenant/email cuando invite falla (404/403)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 13:27:55 -05:00
parent 1bb3808f72
commit 02a5ac07ed

View File

@@ -78,25 +78,48 @@ class InviteService:
# Crear invite en el Hub para que el usuario use el form del workspace
hub_invite_token: Optional[str] = None
invite_url: str = ""
try:
async with httpx.AsyncClient(timeout=10.0) as client:
login_resp = await client.post(
f"{settings.HUB_URL}api/v1/auth/login",
json={"username": settings.HUB_ADMIN_EMAIL, "password": settings.HUB_ADMIN_PASSWORD},
)
if login_resp.status_code == 200:
svc_token = login_resp.json().get("access_token", "")
hub_resp = await client.post(
f"{settings.HUB_URL}api/v1/hub/invites",
json={"email": str(data.email), "tenant_slug": tenant_slug},
headers={"Authorization": f"Bearer {svc_token}"},
if not settings.HUB_ADMIN_EMAIL or not settings.HUB_ADMIN_PASSWORD:
logger.error(
"[invite] HUB_ADMIN_EMAIL o HUB_ADMIN_PASSWORD no configurados — "
"el invite NO se creará en el Hub y se usará el fallback local. "
"Configura HUB_ADMIN_EMAIL y HUB_ADMIN_PASSWORD en .env"
)
else:
try:
async with httpx.AsyncClient(timeout=10.0) as client:
login_resp = await client.post(
f"{settings.HUB_URL}api/v1/auth/login",
json={"username": settings.HUB_ADMIN_EMAIL, "password": settings.HUB_ADMIN_PASSWORD},
)
if hub_resp.status_code in (200, 201):
hub_data = hub_resp.json()
hub_invite_token = hub_data.get("invite_token") or _extract_token_from_url(hub_data.get("invite_url", ""))
invite_url = hub_data.get("invite_url", "")
except Exception as exc:
logger.warning("Hub invite creation failed (non-blocking): %s", exc)
if login_resp.status_code != 200:
logger.error(
"[invite] Hub login falló: status=%s body=%s"
"verifica HUB_ADMIN_EMAIL=%s y HUB_ADMIN_PASSWORD en .env",
login_resp.status_code,
login_resp.text[:200],
settings.HUB_ADMIN_EMAIL,
)
else:
svc_token = login_resp.json().get("access_token", "")
hub_resp = await client.post(
f"{settings.HUB_URL}api/v1/hub/invites",
json={"email": str(data.email), "tenant_slug": tenant_slug},
headers={"Authorization": f"Bearer {svc_token}"},
)
if hub_resp.status_code in (200, 201):
hub_data = hub_resp.json()
hub_invite_token = hub_data.get("invite_token") or _extract_token_from_url(hub_data.get("invite_url", ""))
invite_url = hub_data.get("invite_url", "")
else:
logger.error(
"[invite] Hub invite creation falló: status=%s body=%s tenant=%s email=%s",
hub_resp.status_code,
hub_resp.text[:300],
tenant_slug,
data.email,
)
except Exception as exc:
logger.error("[invite] Hub invite creation excepción (non-blocking): %s", exc)
# Fallback: URL del workspace (Hub) si la creación de invitación en Hub falló
if not invite_url: