import logging import random import time import httpx from app.config import settings from app.utils.redis import get_redis logger = logging.getLogger("videogen") # In-memory fallback for verification codes _sms_code_store: dict[str, tuple[str, float]] = {} def _generate_code(length: int = 6) -> str: return "".join(random.choices("0123456789", k=length)) async def send_sms(phone: str, code: str) -> bool: """Send SMS verification code. Supports mock mode and real HTTP gateway.""" if settings.SMS_MOCK or not settings.SMS_API_URL: logger.info(f"[SMS MOCK] To={phone}, Code={code}") return True try: payload = { "phone": phone, "code": code, "sign_name": settings.SMS_SIGN_NAME, "template_code": settings.SMS_TEMPLATE_CODE, } headers = { "Authorization": f"Bearer {settings.SMS_API_KEY}", "Content-Type": "application/json", } async with httpx.AsyncClient(timeout=10) as client: resp = await client.post(settings.SMS_API_URL, json=payload, headers=headers) resp.raise_for_status() return True except Exception: logger.exception(f"SMS send failed for {phone}") return False async def store_sms_code(phone: str, code: str, ttl: int = 300) -> None: """Store SMS verification code with TTL (default 5 minutes).""" redis = get_redis() if redis: await redis.setex(f"sms_code:{phone}", ttl, code) else: _sms_code_store[phone] = (code, time.time() + ttl) async def generate_and_send_sms(phone: str) -> bool: """Generate a code, store it, and send it via SMS.""" code = _generate_code() ok = await send_sms(phone, code) if ok: await store_sms_code(phone, code) return ok async def verify_sms_code(phone: str, code: str) -> bool: """Verify an SMS verification code.""" redis = get_redis() if redis: stored = await redis.get(f"sms_code:{phone}") if stored and stored == code: await redis.delete(f"sms_code:{phone}") return True return False else: entry = _sms_code_store.pop(phone, None) if entry: stored_code, expires = entry if time.time() < expires and stored_code == code: return True return False