From 5dd3b9f8944061a37feb1e24a04bc8c84ba481f0 Mon Sep 17 00:00:00 2001
From: patrick-motsch
Date: Tue, 17 Feb 2026 16:33:21 +0100
Subject: [PATCH] fix: add per-variant test-auth endpoints to avoid Azure
timeout
Co-authored-by: Cursor
---
.../features/teamsbot/routeFeatureTeamsbot.py | 119 +++++++++++++++++-
1 file changed, 116 insertions(+), 3 deletions(-)
diff --git a/modules/features/teamsbot/routeFeatureTeamsbot.py b/modules/features/teamsbot/routeFeatureTeamsbot.py
index db222efb..94683cfc 100644
--- a/modules/features/teamsbot/routeFeatureTeamsbot.py
+++ b/modules/features/teamsbot/routeFeatureTeamsbot.py
@@ -788,7 +788,7 @@ async def testAuth(
logger.warning(f"[test-auth] No credentials provided and no system bot in DB")
credentialDebug["source"] = "noneFound"
- # Forward to browser bot service
+ # Forward to browser bot service (single all-in-one call — may timeout with many variants)
browserBotUrl = effectiveConfig._getEffectiveBrowserBotUrl()
if not browserBotUrl:
raise HTTPException(status_code=503, detail="Browser Bot URL not configured")
@@ -799,9 +799,8 @@ async def testAuth(
"botAccountEmail": email,
"botAccountPassword": password,
}
-
try:
- timeout = aiohttp.ClientTimeout(total=900)
+ timeout = aiohttp.ClientTimeout(total=210)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(f"{browserBotUrl}/api/bot/test-auth", json=payload) as resp:
if resp.status == 200:
@@ -817,6 +816,120 @@ async def testAuth(
raise HTTPException(status_code=503, detail=f"Browser Bot connection failed: {str(e)}")
+@router.get("/{instanceId}/test-auth/variants")
+@limiter.limit("10/minute")
+async def getTestAuthVariants(
+ request: Request,
+ instanceId: str,
+ context: RequestContext = Depends(getRequestContext),
+):
+ """
+ Get list of available test variant IDs from the Browser Bot.
+ Frontend calls this once, then runs each variant individually.
+ """
+ import aiohttp
+
+ _validateInstanceAccess(instanceId, context)
+ effectiveConfig = _getInstanceConfig(instanceId)
+ browserBotUrl = effectiveConfig._getEffectiveBrowserBotUrl()
+ if not browserBotUrl:
+ raise HTTPException(status_code=503, detail="Browser Bot URL not configured")
+
+ browserBotUrl = browserBotUrl.rstrip("/")
+ try:
+ timeout = aiohttp.ClientTimeout(total=30)
+ async with aiohttp.ClientSession(timeout=timeout) as session:
+ async with session.get(f"{browserBotUrl}/api/bot/test-auth/variants") as resp:
+ if resp.status == 200:
+ return await resp.json()
+ else:
+ errorText = await resp.text()
+ raise HTTPException(status_code=resp.status, detail=f"Browser Bot error: {errorText}")
+ except aiohttp.ClientError as e:
+ logger.error(f"Get variants error: {e}")
+ raise HTTPException(status_code=503, detail=f"Browser Bot connection failed: {str(e)}")
+
+
+@router.post("/{instanceId}/test-auth/variant")
+@limiter.limit("10/minute")
+async def testAuthSingleVariant(
+ request: Request,
+ instanceId: str,
+ context: RequestContext = Depends(getRequestContext),
+):
+ """
+ Run a single test variant. Frontend calls this once per variant (sequentially).
+ Each call stays within Azure's 240s timeout.
+ """
+ import aiohttp
+
+ mandateId = _validateInstanceAccess(instanceId, context)
+ interface = _getInterface(context, instanceId)
+ effectiveConfig = _getInstanceConfig(instanceId)
+
+ body = await request.json()
+ variantId = body.get("variantId")
+ meetingUrl = body.get("meetingUrl")
+ if not variantId or not meetingUrl:
+ raise HTTPException(status_code=400, detail="variantId and meetingUrl are required")
+
+ # Load credentials (same logic as testAuth)
+ email = body.get("botEmail")
+ password = body.get("botPassword")
+ credentialDebug = {"mandateId": mandateId, "source": "none"}
+
+ if email and password:
+ credentialDebug["source"] = "requestBody"
+ credentialDebug["botEmail"] = email
+ else:
+ # Fallback: load from DB
+ try:
+ from modules.shared.configuration import decryptValue
+ systemBot = interface.getActiveSystemBot(mandateId)
+ if not systemBot:
+ systemBot = interface.getActiveSystemBot(None)
+ if systemBot:
+ credentialDebug["searchStrategy"] = "anyMandate"
+ if systemBot:
+ email = systemBot.get("email")
+ encryptedPwd = systemBot.get("encryptedPassword")
+ if email and encryptedPwd:
+ password = decryptValue(encryptedPwd, userId=str(context.user.id), keyName="systemBotPassword")
+ credentialDebug["source"] = "database"
+ credentialDebug["botEmail"] = email
+ credentialDebug["botFound"] = True
+ except Exception as e:
+ logger.warning(f"[test-auth-variant] Could not load system bot: {e}")
+
+ browserBotUrl = effectiveConfig._getEffectiveBrowserBotUrl()
+ if not browserBotUrl:
+ raise HTTPException(status_code=503, detail="Browser Bot URL not configured")
+
+ browserBotUrl = browserBotUrl.rstrip("/")
+ payload = {
+ "variantId": variantId,
+ "meetingUrl": meetingUrl,
+ "botAccountEmail": email,
+ "botAccountPassword": password,
+ }
+
+ try:
+ timeout = aiohttp.ClientTimeout(total=180)
+ async with aiohttp.ClientSession(timeout=timeout) as session:
+ async with session.post(f"{browserBotUrl}/api/bot/test-auth/variant", json=payload) as resp:
+ if resp.status == 200:
+ result = await resp.json()
+ result["credentialDebug"] = credentialDebug
+ return result
+ else:
+ errorText = await resp.text()
+ logger.error(f"[test-auth-variant] {variantId} failed: {resp.status}")
+ raise HTTPException(status_code=resp.status, detail=f"Browser Bot error: {errorText}")
+ except aiohttp.ClientError as e:
+ logger.error(f"[test-auth-variant] {variantId} connection error: {e}")
+ raise HTTPException(status_code=503, detail=f"Browser Bot connection failed: {str(e)}")
+
+
# =========================================================================
# Browser Bot Communication Endpoints (HTTP Fallback + WebSocket)
# =========================================================================