from fastapi import APIRouter, HTTPException from app.models import PasswordResetConfirm, PasswordResetRequest from app.services.auth_service import ( consume_password_reset_otp, create_password_reset_otp, get_user_by_username, update_user_password, ) from app.services.email_service import send_email router = APIRouter(prefix="/api/password-reset") @router.post("/request") def request_reset(payload: PasswordResetRequest): email = payload.email.strip() if not email: raise HTTPException(status_code=400, detail="Email is required") user = get_user_by_username(email) if not user: return {"ok": True} otp = create_password_reset_otp(email) body = ( "Hi,\n\n" "We received a request to reset your Quantfortune password.\n\n" f"Your OTP code is: {otp}\n" "This code is valid for 10 minutes.\n\n" "If you did not request this, you can ignore this email.\n\n" "Quantfortune Support" ) try: ok = send_email(email, "Quantfortune Password Reset OTP", body) except Exception as exc: raise HTTPException(status_code=500, detail=f"Email send failed: {exc}") from exc if not ok: raise HTTPException(status_code=500, detail="Email send failed: SMTP not configured") return {"ok": True} @router.post("/confirm") def confirm_reset(payload: PasswordResetConfirm): email = payload.email.strip() otp = payload.otp.strip() new_password = payload.new_password if not email or not otp or not new_password: raise HTTPException(status_code=400, detail="Email, OTP, and new password are required") user = get_user_by_username(email) if not user: raise HTTPException(status_code=400, detail="Invalid OTP or email") if not consume_password_reset_otp(email, otp): raise HTTPException(status_code=400, detail="Invalid or expired OTP") update_user_password(user["id"], new_password) return {"ok": True}