import random import string from typing import Optional import redis import json from app.core.config import settings # Mock Redis client for development class MockRedisClient: def __init__(self): self.data = {} def setex(self, key: str, time: int, value: str): self.data[key] = value return True def get(self, key: str) -> Optional[str]: return self.data.get(key) def delete(self, key: str): if key in self.data: del self.data[key] return True try: redis_client = redis.from_url(settings.REDIS_URL) redis_client.ping() except Exception: redis_client = MockRedisClient() def generate_otp() -> str: return "".join(random.choices(string.digits, k=6)) def store_otp(phone_number: str, otp: str) -> bool: try: otp_data = {"otp": otp, "attempts": 0} redis_client.setex( f"otp:{phone_number}", settings.OTP_EXPIRE_MINUTES * 60, json.dumps(otp_data), ) return True except Exception: return False def verify_otp(phone_number: str, otp: str) -> bool: try: stored_data = redis_client.get(f"otp:{phone_number}") if not stored_data: return False otp_data = json.loads(stored_data) if otp_data["attempts"] >= settings.OTP_MAX_ATTEMPTS: redis_client.delete(f"otp:{phone_number}") return False if otp_data["otp"] == otp: redis_client.delete(f"otp:{phone_number}") return True else: otp_data["attempts"] += 1 redis_client.setex( f"otp:{phone_number}", settings.OTP_EXPIRE_MINUTES * 60, json.dumps(otp_data), ) return False except Exception: return False def send_otp(phone_number: str, otp: str) -> bool: # Mock SMS sending for development print(f"Sending OTP {otp} to {phone_number}") return True