138 lines
3.8 KiB
Python
138 lines
3.8 KiB
Python
from typing import List, Dict, Optional, Union, Any
|
|
from datetime import datetime
|
|
import re
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import or_
|
|
from models.user import User
|
|
from schemas.user import UserCreate, UserUpdate
|
|
from passlib.context import CryptContext
|
|
|
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
|
|
|
def validate_username(username: str) -> bool:
|
|
"""
|
|
Validate username format and length.
|
|
|
|
Args:
|
|
username: Username to validate
|
|
|
|
Returns:
|
|
bool: True if valid, False otherwise
|
|
"""
|
|
pattern = r'^[a-zA-Z0-9_]{3,32}$'
|
|
return bool(re.match(pattern, username))
|
|
|
|
def hash_password(password: str) -> str:
|
|
"""
|
|
Hash a password using bcrypt.
|
|
|
|
Args:
|
|
password: Plain text password
|
|
|
|
Returns:
|
|
str: Hashed password
|
|
"""
|
|
return pwd_context.hash(password)
|
|
|
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
"""
|
|
Verify a password against its hash.
|
|
|
|
Args:
|
|
plain_password: Password to verify
|
|
hashed_password: Stored hash to check against
|
|
|
|
Returns:
|
|
bool: True if password matches
|
|
"""
|
|
return pwd_context.verify(plain_password, hashed_password)
|
|
|
|
def search_users(
|
|
db: Session,
|
|
search_term: str,
|
|
skip: int = 0,
|
|
limit: int = 10
|
|
) -> List[User]:
|
|
"""
|
|
Search for users by username, first name, or last name.
|
|
|
|
Args:
|
|
db: Database session
|
|
search_term: Term to search for
|
|
skip: Number of records to skip
|
|
limit: Max number of records to return
|
|
|
|
Returns:
|
|
List of matching users
|
|
"""
|
|
return db.query(User).filter(
|
|
or_(
|
|
User.username.ilike(f"%{search_term}%"),
|
|
User.first_name.ilike(f"%{search_term}%"),
|
|
User.last_name.ilike(f"%{search_term}%")
|
|
)
|
|
).offset(skip).limit(limit).all()
|
|
|
|
def update_last_login(db: Session, user: User) -> User:
|
|
"""
|
|
Update user's last login timestamp.
|
|
|
|
Args:
|
|
db: Database session
|
|
user: User to update
|
|
|
|
Returns:
|
|
Updated user object
|
|
"""
|
|
user.last_login = datetime.utcnow()
|
|
db.commit()
|
|
db.refresh(user)
|
|
return user
|
|
|
|
def format_user_profile(user: User) -> Dict[str, Any]:
|
|
"""
|
|
Format user data for profile display.
|
|
|
|
Args:
|
|
user: User object to format
|
|
|
|
Returns:
|
|
Dict containing formatted user profile data
|
|
"""
|
|
return {
|
|
"username": user.username,
|
|
"full_name": f"{user.first_name or ''} {user.last_name or ''}".strip(),
|
|
"bio": user.bio or "No bio available",
|
|
"profile_picture": user.profile_picture or "default.jpg",
|
|
"relationship_status": user.relationship_status or "Not specified",
|
|
"interests": user.interests.split(",") if user.interests else [],
|
|
"last_active": user.last_login.isoformat() if user.last_login else None
|
|
}
|
|
|
|
def validate_user_update(user_data: UserUpdate) -> Dict[str, str]:
|
|
"""
|
|
Validate user update data.
|
|
|
|
Args:
|
|
user_data: Update data to validate
|
|
|
|
Returns:
|
|
Dict with error messages if validation fails
|
|
"""
|
|
errors = {}
|
|
|
|
if user_data.username and not validate_username(user_data.username):
|
|
errors["username"] = "Invalid username format"
|
|
|
|
if user_data.email and not validate_email(user_data.email):
|
|
errors["email"] = "Invalid email format"
|
|
|
|
if user_data.profile_picture and not user_data.profile_picture.endswith(('.jpg','.png','.jpeg')):
|
|
errors["profile_picture"] = "Invalid image format"
|
|
|
|
if user_data.relationship_status and user_data.relationship_status not in [
|
|
"Single", "In a relationship", "Married", "It's complicated"
|
|
]:
|
|
errors["relationship_status"] = "Invalid relationship status"
|
|
|
|
return errors |