""" Bing Webmaster OAuth2 Routes Handles Bing Webmaster Tools OAuth2 authentication flow. """ from fastapi import APIRouter, Depends, HTTPException, status, Query from fastapi.responses import RedirectResponse, HTMLResponse from typing import Dict, Any, Optional from pydantic import BaseModel from loguru import logger from services.integrations.bing_oauth import BingOAuthService from middleware.auth_middleware import get_current_user router = APIRouter(prefix="/bing", tags=["Bing Webmaster OAuth"]) # Initialize OAuth service oauth_service = BingOAuthService() # Pydantic Models class BingOAuthResponse(BaseModel): auth_url: str state: str class BingCallbackResponse(BaseModel): success: bool message: str access_token: Optional[str] = None expires_in: Optional[int] = None class BingStatusResponse(BaseModel): connected: bool sites: list total_sites: int @router.get("/auth/url", response_model=BingOAuthResponse) async def get_bing_auth_url( user: Dict[str, Any] = Depends(get_current_user) ): """Get Bing Webmaster OAuth2 authorization URL.""" try: user_id = user.get('id') if not user_id: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.") auth_data = oauth_service.generate_authorization_url(user_id) if not auth_data: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Bing Webmaster OAuth is not properly configured. Please check that BING_CLIENT_ID and BING_CLIENT_SECRET environment variables are set with valid Bing Webmaster application credentials." ) return BingOAuthResponse(**auth_data) except Exception as e: logger.error(f"Error generating Bing Webmaster OAuth URL: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to generate Bing Webmaster OAuth URL." ) @router.get("/callback") async def handle_bing_callback( code: str = Query(..., description="Authorization code from Bing"), state: str = Query(..., description="State parameter for security"), error: Optional[str] = Query(None, description="Error from Bing OAuth") ): """Handle Bing Webmaster OAuth2 callback.""" try: if error: logger.error(f"Bing Webmaster OAuth error: {error}") html_content = f""" Bing Webmaster Connection Failed

Connection Failed

There was an error connecting to Bing Webmaster Tools.

You can close this window and try again.

""" return HTMLResponse(content=html_content, headers={ "Cross-Origin-Opener-Policy": "unsafe-none", "Cross-Origin-Embedder-Policy": "unsafe-none" }) if not code or not state: logger.error("Missing code or state parameter in Bing Webmaster OAuth callback") html_content = """ Bing Webmaster Connection Failed

Connection Failed

Missing required parameters.

You can close this window and try again.

""" return HTMLResponse(content=html_content, headers={ "Cross-Origin-Opener-Policy": "unsafe-none", "Cross-Origin-Embedder-Policy": "unsafe-none" }) # Exchange code for token result = oauth_service.handle_oauth_callback(code, state) if not result or not result.get('success'): logger.error("Failed to exchange Bing Webmaster OAuth code for token") html_content = """ Bing Webmaster Connection Failed

Connection Failed

Failed to exchange authorization code for access token.

You can close this window and try again.

""" return HTMLResponse(content=html_content) # Create Bing insights task immediately after successful connection try: from services.database import SessionLocal from services.platform_insights_monitoring_service import create_platform_insights_task # Get user_id from state (stored during OAuth flow) db = SessionLocal() try: # Get user_id from Bing OAuth service state lookup import sqlite3 with sqlite3.connect(oauth_service.db_path) as conn: cursor = conn.cursor() cursor.execute('SELECT user_id FROM bing_oauth_states WHERE state = ?', (state,)) result_db = cursor.fetchone() if result_db: user_id = result_db[0] # Don't fetch site_url here - it requires API calls # The executor will fetch it when the task runs (weekly) # Create insights task without site_url to avoid API calls task_result = create_platform_insights_task( user_id=user_id, platform='bing', site_url=None, # Will be fetched by executor when task runs db=db ) if task_result.get('success'): logger.info(f"Created Bing insights task for user {user_id}") else: logger.warning(f"Failed to create Bing insights task: {task_result.get('error')}") finally: db.close() except Exception as e: # Non-critical: log but don't fail OAuth callback logger.warning(f"Failed to create Bing insights task after OAuth: {e}") # Return success page with postMessage script html_content = f""" Bing Webmaster Connection Successful

Connection Successful!

Your Bing Webmaster Tools account has been connected successfully.

You can close this window now.

""" return HTMLResponse(content=html_content, headers={ "Cross-Origin-Opener-Policy": "unsafe-none", "Cross-Origin-Embedder-Policy": "unsafe-none" }) except Exception as e: logger.error(f"Error handling Bing Webmaster OAuth callback: {e}") html_content = """ Bing Webmaster Connection Failed

Connection Failed

An unexpected error occurred during connection.

You can close this window and try again.

""" return HTMLResponse(content=html_content, headers={ "Cross-Origin-Opener-Policy": "unsafe-none", "Cross-Origin-Embedder-Policy": "unsafe-none" }) @router.get("/status", response_model=BingStatusResponse) async def get_bing_oauth_status( user: Dict[str, Any] = Depends(get_current_user) ): """Get Bing Webmaster OAuth connection status.""" try: user_id = user.get('id') if not user_id: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.") status_data = oauth_service.get_connection_status(user_id) return BingStatusResponse(**status_data) except Exception as e: logger.error(f"Error getting Bing Webmaster OAuth status: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to get Bing Webmaster connection status." ) @router.delete("/disconnect/{token_id}") async def disconnect_bing_site( token_id: int, user: Dict[str, Any] = Depends(get_current_user) ): """Disconnect a Bing Webmaster site.""" try: user_id = user.get('id') if not user_id: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.") success = oauth_service.revoke_token(user_id, token_id) if not success: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Bing Webmaster token not found or could not be disconnected." ) return {"success": True, "message": f"Bing Webmaster site disconnected successfully."} except Exception as e: logger.error(f"Error disconnecting Bing Webmaster site: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to disconnect Bing Webmaster site." ) @router.get("/health") async def bing_oauth_health(): """Bing Webmaster OAuth health check.""" return { "status": "healthy", "service": "bing_oauth", "timestamp": "2024-01-01T00:00:00Z", "version": "1.0.0" } @router.post("/purge-expired") async def purge_expired_bing_tokens( user: Dict[str, Any] = Depends(get_current_user) ): """Purge user's expired/inactive Bing tokens to avoid refresh loops before reauth.""" try: user_id = user.get('id') if not user_id: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.") deleted = oauth_service.purge_expired_tokens(user_id) return { "success": True, "purged": deleted, "message": f"Purged {deleted} expired/inactive Bing tokens" } except Exception as e: logger.error(f"Error purging expired Bing tokens: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to purge expired Bing tokens." )