60 lines
1.9 KiB
Python
60 lines
1.9 KiB
Python
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}
|