import uuid from datetime import datetime, timedelta, timezone from fastapi import APIRouter, Request, Depends from fastapi.responses import RedirectResponse from web.templates_env import templates from sqlalchemy.orm import Session from web.auth import hash_password from web.config import settings from web.database import get_db from web.models import User from web.schemas import validate_reset_password router = APIRouter() @router.get("/forgot-password") def forgot_form(request: Request): return templates.TemplateResponse("forgot_password.html", {"request": request, "user": None}) @router.post("/forgot-password") async def forgot_submit(request: Request, db: Session = Depends(get_db)): form = await request.form() email = form.get("email", "").strip() if email: user = db.query(User).filter(User.email == email).first() if user: token = uuid.uuid4().hex user.password_reset_token = token user.password_reset_expires = datetime.now(timezone.utc) + timedelta( minutes=settings.PASSWORD_RESET_EXPIRE_MINUTES ) db.commit() reset_url = f"{settings.BASE_URL}/reset-password?token={token}" print("=" * 40) print("СБРОС ПАРОЛЯ") print(f"Пользователь: {user.email}") print(f"Ссылка: {reset_url}") print(f"Действительна: {settings.PASSWORD_RESET_EXPIRE_MINUTES} мин.") print("=" * 40) return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Сброс пароля", "message": "Если аккаунт с таким email существует, мы отправили письмо со ссылкой для сброса пароля.", }) @router.get("/reset-password") def reset_form(request: Request, token: str, db: Session = Depends(get_db)): user = db.query(User).filter(User.password_reset_token == token).first() if not user or not user.password_reset_expires: return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Ошибка", "message": "Неверная или устаревшая ссылка.", }) if datetime.now(timezone.utc) > user.password_reset_expires.replace(tzinfo=timezone.utc): return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Ошибка", "message": "Срок действия ссылки истек.", }) return templates.TemplateResponse("reset_password.html", { "request": request, "user": None, "token": token, }) @router.post("/reset-password") async def reset_submit(request: Request, token: str, db: Session = Depends(get_db)): user = db.query(User).filter(User.password_reset_token == token).first() if not user or not user.password_reset_expires: return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Ошибка", "message": "Неверная или устаревшая ссылка.", }) if datetime.now(timezone.utc) > user.password_reset_expires.replace(tzinfo=timezone.utc): return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Ошибка", "message": "Срок действия ссылки истек.", }) form = await request.form() data = dict(form) errors = validate_reset_password(data) if errors: return templates.TemplateResponse("reset_password.html", { "request": request, "user": None, "token": token, "errors": errors, }) user.password_hash = hash_password(data["password"]) user.password_reset_token = None user.password_reset_expires = None db.commit() return templates.TemplateResponse("message.html", { "request": request, "user": None, "title": "Пароль изменен", "message": "Ваш пароль успешно изменен. Теперь вы можете войти.", "link": "/login", "link_text": "Войти", })