from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from typing import Optional from sqlalchemy.orm import Session import traceback from ..services.postgresql_manager import PostgreSQLManager from ..utils.config import config from ..services.garmin.client import GarminClient router = APIRouter() def get_db(): db_manager = PostgreSQLManager(config.DATABASE_URL) with db_manager.get_db_session() as session: yield session class GarminCredentials(BaseModel): username: str password: str is_china: bool = False class FitbitCredentials(BaseModel): client_id: str client_secret: str class FitbitCallback(BaseModel): callback_url: str class GarminMFARequest(BaseModel): verification_code: str session_id: str class AuthStatusResponse(BaseModel): garmin: Optional[dict] = None fitbit: Optional[dict] = None @router.get("/setup/auth-status", response_model=AuthStatusResponse) async def get_auth_status(db: Session = Depends(get_db)): # This would return the current authentication status from the database # Implementation will connect with the services layer # For now, return placeholder until we have full implementation return AuthStatusResponse( garmin={ "username": "example@example.com", "authenticated": False, "token_expires_at": None, "last_login": None, "is_china": False }, fitbit={ "client_id": "example_client_id", "authenticated": False, "token_expires_at": None, "last_login": None } ) @router.post("/setup/garmin") async def save_garmin_credentials(credentials: GarminCredentials, db: Session = Depends(get_db)): from ..utils.helpers import setup_logger logger = setup_logger(__name__) # This would save the Garmin credentials and attempt login # Implementation will connect with the services layer logger.info(f"Received Garmin credentials for user: {credentials.username}, is_china: {credentials.is_china}") # Create the client with credentials but don't trigger login in __init__ if we handle it separately garmin_client = GarminClient(credentials.username, credentials.password, credentials.is_china) logger.debug("GarminClient instance created successfully") try: logger.debug("Attempting to log in to Garmin") garmin_client.login() # If login is successful, we're done logger.info(f"Successfully authenticated Garmin user: {credentials.username}") return {"status": "success", "message": "Garmin credentials saved and authenticated successfully"} except Exception as e: logger.error(f"Error during Garmin authentication: {str(e)}") logger.error(f"Exception type: {type(e).__name__}") logger.error(f"Exception details: {repr(e)}") import traceback logger.error(f"Full traceback: {traceback.format_exc()}") if "MFA" in str(e) or "mfa" in str(e).lower() or "MFA Required" in str(e): logger.info("MFA required for Garmin authentication") # Initiate MFA process and get session ID session_id = garmin_client.initiate_mfa(credentials.username) return {"status": "mfa_required", "message": "Multi-factor authentication required", "session_id": session_id} else: logger.error(f"Authentication failed with error: {str(e)}") return {"status": "error", "message": f"Error during authentication: {str(e)}"} @router.post("/setup/garmin/mfa") async def complete_garmin_mfa(mfa_request: GarminMFARequest, db: Session = Depends(get_db)): from ..utils.helpers import setup_logger logger = setup_logger(__name__) # Complete the MFA process for Garmin using session ID logger.info(f"Received MFA verification code for session {mfa_request.session_id}: {'*' * len(mfa_request.verification_code)}") try: # Create a basic Garmin client without credentials - we'll use the session data garmin_client = GarminClient() logger.debug(f"Attempting to handle MFA for session: {mfa_request.session_id}") # Call the handle_mfa method which will use database-stored session data success = garmin_client.handle_mfa(mfa_request.verification_code, session_id=mfa_request.session_id) if success: logger.info(f"MFA verification completed successfully for session: {mfa_request.session_id}") return {"status": "success", "message": "MFA verification completed successfully"} else: logger.error(f"MFA verification failed for session: {mfa_request.session_id}") return {"status": "error", "message": "MFA verification failed"} except Exception as e: logger.error(f"MFA verification failed for session {mfa_request.session_id} with exception: {str(e)}") logger.error(f"Exception type: {type(e).__name__}") logger.error(f"Exception details: {repr(e)}") import traceback logger.error(f"Full traceback: {traceback.format_exc()}") return {"status": "error", "message": f"MFA verification failed: {str(e)}"} @router.post("/setup/fitbit") async def save_fitbit_credentials(credentials: FitbitCredentials, db: Session = Depends(get_db)): # This would save the Fitbit credentials and return auth URL # Implementation will connect with the services layer return { "status": "success", "auth_url": "https://www.fitbit.com/oauth2/authorize?...", "message": "Fitbit credentials saved, please visit auth_url to authorize" } @router.post("/setup/fitbit/callback") async def fitbit_callback(callback_data: FitbitCallback, db: Session = Depends(get_db)): # This would handle the Fitbit OAuth callback # Implementation will connect with the services layer return {"status": "success", "message": "Fitbit OAuth flow completed successfully"}