Fix broker reconnect dashboard redirect

This commit is contained in:
Thigazhezhilan J 2026-04-14 09:54:19 +05:30
parent b5b759c5ed
commit a90603f4f6
2 changed files with 125 additions and 1 deletions

View File

@ -1,5 +1,6 @@
import os
from datetime import datetime, timedelta
from urllib.parse import urlsplit, urlunsplit
from fastapi import APIRouter, HTTPException, Query, Request
from fastapi.responses import RedirectResponse
@ -53,6 +54,7 @@ from app.services.zerodha_storage import (
)
router = APIRouter(prefix="/api/broker")
DEFAULT_PRODUCTION_DASHBOARD_URL = "https://app.quantfortune.com/dashboard?armed=false"
def _require_user(request: Request):
@ -72,6 +74,31 @@ def _require_session_id(request: Request) -> str:
return session_id
def _build_broker_dashboard_url(request: Request) -> str:
configured_dashboard = (os.getenv("BROKER_DASHBOARD_URL") or "").strip()
if configured_dashboard:
return configured_dashboard
app_base_url = (os.getenv("APP_BASE_URL") or "").strip()
if app_base_url:
return f"{app_base_url.rstrip('/')}/dashboard?armed=false"
base_url = str(request.base_url).rstrip("/")
parsed = urlsplit(base_url)
hostname = (parsed.hostname or "").strip().lower()
scheme = parsed.scheme or "https"
if hostname in {"localhost", "127.0.0.1"}:
return f"{scheme}://{hostname}:5173/dashboard?armed=false"
if hostname.startswith("api.") and len(hostname) > 4:
scheme = "https"
frontend_netloc = parsed.netloc.replace(hostname, f"app.{hostname[4:]}", 1)
return urlunsplit((scheme, frontend_netloc, "/dashboard", "armed=false", ""))
return DEFAULT_PRODUCTION_DASHBOARD_URL
def _first_number(*values, default: float = 0.0) -> float:
for value in values:
try:
@ -654,7 +681,7 @@ async def broker_callback(request: Request, request_token: str = "", state: str
broker_user_id=session_data.get("user_id"),
auth_state="VALID",
)
target_url = os.getenv("BROKER_DASHBOARD_URL") or "/dashboard?armed=false"
target_url = _build_broker_dashboard_url(request)
return RedirectResponse(target_url)

View File

@ -173,6 +173,103 @@ def test_wrong_or_expired_broker_callback_state_fails(monkeypatch):
assert response.json() == {"detail": "Invalid or expired broker callback state"}
def test_reconnect_callback_redirects_to_app_dashboard_by_default(monkeypatch):
import app.main as app_main
import app.routers.broker as broker_router
monkeypatch.setenv("APP_ENV", "test")
monkeypatch.setenv("DISABLE_STARTUP_TASKS", "1")
monkeypatch.setenv("CORS_ORIGINS", "http://localhost:3000")
monkeypatch.delenv("BROKER_DASHBOARD_URL", raising=False)
monkeypatch.delenv("APP_BASE_URL", raising=False)
importlib.reload(app_main)
app = app_main.create_app()
client = TestClient(app)
monkeypatch.setattr(broker_router, "get_user_for_session", lambda _sid: {"id": "user-1", "username": "user@example.com"})
monkeypatch.setattr(
broker_router,
"consume_broker_callback_state",
lambda **kwargs: {"id": "state-1", "expires_at": datetime.now(timezone.utc).isoformat()},
)
monkeypatch.setattr(
broker_router,
"get_broker_credentials",
lambda _user_id: {"api_key": "kite-key", "api_secret": "kite-secret"},
)
monkeypatch.setattr(
broker_router,
"exchange_request_token",
lambda api_key, api_secret, token: {
"access_token": "access-token",
"request_token": token,
"user_name": "Trader",
"user_id": "Z123",
},
)
monkeypatch.setattr(broker_router, "set_zerodha_session", lambda user_id, payload: None)
monkeypatch.setattr(broker_router, "set_connected_broker", lambda user_id, broker, token, **kwargs: None)
response = client.get(
"/api/broker/callback",
params={"request_token": "request-token", "state": "valid-state"},
cookies={"session_id": "session-1"},
follow_redirects=False,
headers={"host": "api.quantfortune.com"},
)
assert response.status_code == 307
assert response.headers["location"] == "https://app.quantfortune.com/dashboard?armed=false"
def test_reconnect_callback_uses_configured_dashboard_url(monkeypatch):
import app.main as app_main
import app.routers.broker as broker_router
monkeypatch.setenv("APP_ENV", "test")
monkeypatch.setenv("DISABLE_STARTUP_TASKS", "1")
monkeypatch.setenv("CORS_ORIGINS", "http://localhost:3000")
monkeypatch.setenv("BROKER_DASHBOARD_URL", "https://app.quantfortune.com/dashboard?armed=false")
importlib.reload(app_main)
app = app_main.create_app()
client = TestClient(app)
monkeypatch.setattr(broker_router, "get_user_for_session", lambda _sid: {"id": "user-1", "username": "user@example.com"})
monkeypatch.setattr(
broker_router,
"consume_broker_callback_state",
lambda **kwargs: {"id": "state-1", "expires_at": datetime.now(timezone.utc).isoformat()},
)
monkeypatch.setattr(
broker_router,
"get_broker_credentials",
lambda _user_id: {"api_key": "kite-key", "api_secret": "kite-secret"},
)
monkeypatch.setattr(
broker_router,
"exchange_request_token",
lambda api_key, api_secret, token: {
"access_token": "access-token",
"request_token": token,
"user_name": "Trader",
"user_id": "Z123",
},
)
monkeypatch.setattr(broker_router, "set_zerodha_session", lambda user_id, payload: None)
monkeypatch.setattr(broker_router, "set_connected_broker", lambda user_id, broker, token, **kwargs: None)
response = client.get(
"/api/broker/callback",
params={"request_token": "request-token", "state": "valid-state"},
cookies={"session_id": "session-1"},
follow_redirects=False,
headers={"host": "api.quantfortune.com"},
)
assert response.status_code == 307
assert response.headers["location"] == "https://app.quantfortune.com/dashboard?armed=false"
def test_broker_callback_state_service_rejects_wrong_user_and_expired_state(monkeypatch):
import app.services.broker_callback_state as callback_state