138 lines
5.9 KiB
Python
138 lines
5.9 KiB
Python
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"} |