Files
api/routes/auth/current_user.py
2026-01-17 15:21:41 -05:00

192 lines
8.2 KiB
Python

from fastapi import Depends, HTTPException, status, APIRouter
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update
import logging
from database import get_db
from models import Account_User, Customer_Customer, Customer_Description, Card
from schemas import TokenData, CustomerUpdate
from jose import JWTError, jwt
from config import load_config
logger = logging.getLogger(__name__)
# Load JWT configuration from environment
ApplicationConfig = load_config()
SECRET_KEY = ApplicationConfig.JWT_SECRET_KEY
ALGORITHM = ApplicationConfig.JWT_ALGORITHM
async def get_current_user(token: str = Depends(OAuth2PasswordBearer(tokenUrl="auth/login")), db: AsyncSession = Depends(get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = await db.execute(select(Account_User).where(Account_User.username == token_data.username))
user = user.scalar_one_or_none()
if user is None:
raise credentials_exception
return user
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")
router = APIRouter()
@router.get("/me")
async def read_users_me(current_user: Account_User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
# Get customer details for the current user using user_id
customer = None
if current_user.user_id:
customer_result = await db.execute(select(Customer_Customer).where(Customer_Customer.id == current_user.user_id))
customer = customer_result.scalar_one_or_none()
# Get house description if exists
house_description = None
if customer:
description_record = await db.execute(
select(Customer_Description).where(Customer_Description.customer_id == customer.id)
)
description_record = description_record.scalar_one_or_none()
if description_record:
house_description = description_record.description
# Map state code to name
state_mapping = {0: "MA", 1: "NH"} # Add more as needed
state_name = state_mapping.get(customer.customer_state, str(customer.customer_state)) if customer else None
return {
"id": current_user.id,
"username": current_user.username,
"email": current_user.email,
"account_number": current_user.account_number,
"customer_first_name": customer.customer_first_name if customer else None,
"customer_last_name": customer.customer_last_name if customer else None,
"customer_address": customer.customer_address if customer else None,
"customer_apt": customer.customer_apt if customer else None,
"customer_town": customer.customer_town if customer else None,
"customer_state": state_name,
"customer_zip": customer.customer_zip if customer else None,
"customer_phone_number": customer.customer_phone_number if customer else None,
"customer_home_type": customer.customer_home_type if customer else None,
"customer_email": customer.customer_email if customer else None,
"house_description": house_description,
"member_since": current_user.member_since,
"last_seen": current_user.last_seen
}
@router.put("/update-customer")
async def update_customer_info(
customer_data: CustomerUpdate,
current_user: Account_User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
if not current_user.user_id:
raise HTTPException(status_code=404, detail="Customer not found")
try:
# Update Customer_Customer table
customer_update_data = customer_data.dict(exclude_unset=True)
customer_fields = {
'customer_first_name', 'customer_last_name', 'customer_address', 'customer_apt',
'customer_town', 'customer_state', 'customer_zip', 'customer_phone_number',
'customer_home_type', 'customer_email'
}
customer_update_dict = {k: v for k, v in customer_update_data.items() if k in customer_fields}
if customer_update_dict:
await db.execute(
update(Customer_Customer)
.where(Customer_Customer.id == current_user.user_id)
.values(**customer_update_dict)
)
# Update house description if provided
if 'house_description' in customer_update_data and customer_update_data['house_description']:
# Get customer record
customer = await db.execute(
select(Customer_Customer).where(Customer_Customer.id == current_user.user_id)
)
customer = customer.scalar_one_or_none()
if customer:
# Check if description record exists
description_record = await db.execute(
select(Customer_Description).where(Customer_Description.customer_id == customer.id)
)
description_record = description_record.scalar_one_or_none()
if description_record:
# Update existing
await db.execute(
update(Customer_Description)
.where(Customer_Description.customer_id == customer.id)
.values(description=customer_update_data['house_description'])
)
else:
# Create new
new_description = Customer_Description(
customer_id=customer.id,
account_number=current_user.account_number,
company_id=customer.company_id or 1,
fill_location=0,
description=customer_update_data['house_description']
)
db.add(new_description)
await db.commit()
# Sync billing info to Authorize.net if address-related fields were updated
billing_fields = {
'customer_first_name', 'customer_last_name', 'customer_address',
'customer_town', 'customer_state', 'customer_zip', 'customer_phone_number',
'customer_email'
}
if any(k in customer_update_dict for k in billing_fields):
# Late import to avoid circular dependency
from routes.payment.routes import update_customer_profile, update_payment_profile_billing
# Refetch the updated customer
customer_result = await db.execute(
select(Customer_Customer).where(Customer_Customer.id == current_user.user_id)
)
updated_customer = customer_result.scalar_one_or_none()
if updated_customer and updated_customer.auth_net_profile_id:
# Update customer profile in Authorize.net
update_customer_profile(updated_customer.auth_net_profile_id, updated_customer)
# Update all saved payment profiles with new billing info
cards_result = await db.execute(
select(Card).where(Card.user_id == updated_customer.id)
)
cards = cards_result.scalars().all()
for card in cards:
if card.auth_net_payment_profile_id:
update_payment_profile_billing(
customer_profile_id=updated_customer.auth_net_profile_id,
payment_profile_id=card.auth_net_payment_profile_id,
customer=updated_customer,
card=card
)
logger.info(f"Synced billing info to Authorize.net for customer {updated_customer.id}")
return {"message": "Customer information updated successfully"}
except Exception as e:
await db.rollback()
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to update customer information: {str(e)}"
)