101:async def google_start_impl(user): 102- """Shared implementation: generate state, persist, return Google's authorization URL.""" 103- try: 104- client = gmail_service.resolve_client_config() 105- except gmail_service.GmailOauthError as e: 106- # Be honest: no client config -> no authorization URL (never fabricate one) 107- raise HTTPException(400, f"Google OAuth is not configured: {e}") 108- state = gmail_service.generate_state() 109- if _oauth_redis is not None: 110- _oauth_redis.setex(f"oauthstate:{state}", 600, json.dumps({"user_id": user.id})) 111- else: 112- _pending_oauth_states[state] = {"user_id": user.id} 113- auth_url = gmail_service.build_authorization_url(client, state) 114- return {"authorization_url": auth_url} 115- 116- 117-_pending_oauth_states: dict = {} 118- 119- 120-@router.get("/google/start") 121-async def google_start(request: Request, user=Depends(get_current_user)): 122: return await google_start_impl(user) 123- 124- 125-@router.get("/google/callback") 126-async def google_callback(request: Request, code: str = "", state: str = "", error: str = "", 127- db: AsyncSession = Depends(get_db)): 128- """Handle Google's response: verify state, exchange code for tokens, store encrypted.""" 129- from app.models import GmailAccount, OAuthClientConfig, OAuthToken 130- 131- if error: 132- return HTMLResponse(f"