feat: Add Password recuperation logic

Added:
- Phase 1 Template + Logic
- Phase 2 Template + Logic
This commit is contained in:
2026-03-20 11:32:54 +01:00
parent 351c9cd955
commit 6f9cb34b6c
16 changed files with 170 additions and 7 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+26 -2
View File
@@ -18,7 +18,8 @@ def enviar_correo_bienvenida(email_usuario: str, nombre_usuario: str):
send_hemail(email_usuario, "Inicio de Sesión correcto", html_content, "Has iniciado sesión...")
@shared_task
def enviar_correo_confirmacion(usuario: User):
def enviar_correo_confirmacion(id: int):
usuario = User.objects.get(id=id)
code = VerificationCode.objects.create(
user = usuario,
code_mode = VerificationCode.VerificationModes.VERIFY_ACCOUNT,
@@ -26,4 +27,27 @@ def enviar_correo_confirmacion(usuario: User):
)
message = verify_message.format(name = usuario.get_full_name(), protocol = settings.PROTOCOL, domain = settings.DOMAIN, code = code.code)
email_result = send_email(usuario.email, "Verificación de cuenta", message)
email_result = send_email(usuario.email, "Verificación de cuenta", message)
@shared_task
def enviar_correo_recuperacion(email: str):
usuario = User.objects.get(email=email)
if usuario is not None:
ver_code = VerificationCode.objects.create(
code_mode = VerificationCode.VerificationModes.RESET_PASSWORD,
user = usuario,
code = ''.join(random.choices(string.digits, k=12))
)
ver_code.save()
html_content = render_to_string(
'emails/reset_pass.html',
{
"name": usuario.get_full_name(),
"domain": settings.DOMAIN,
"protocol": settings.PROTOCOL,
"code": ver_code.code
},
using='jinja2'
)
send_hemail(email, "Reset de Contraseña", html_content, "Estas reseteando la contraseña...")
+1 -1
View File
@@ -34,7 +34,7 @@
</div>
<div class="mt-3 text-center">
<a href="#" class="text-decoration-none">¿Olvidaste tu contraseña?</a>
<a href="{% url 'reset_password' %}" class="text-decoration-none">¿Olvidaste tu contraseña?</a>
</div>
<hr class="my-3">
@@ -0,0 +1,34 @@
{% extends "tienda/base.html" %}
{% load static %}
{% block content %}
<div class="row justify-content-center mt-5">
<div class="col-md-6 col-lg-4">
<div class="card">
<div class="card-header">
<h3 class="text-center mb-0">Recuperar contraseña</h3>
</div>
<div class="card-body">
<form method="post" action="{% url 'reset_password' %}">
{% csrf_token %}
<div class="mb-3">
<label for="loginEmail" class="form-label">Correo Electrónico</label>
<input type="email" class="form-control" id="loginEmail" name="email" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Recuperar contraseña</button>
</div>
<hr class="my-3">
<div class="text-center">
<p class="mb-0">¿No tienes cuenta? <a href="{% url 'register' %}">Regístrate aquí</a></p>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
@@ -0,0 +1,39 @@
{% extends "tienda/base.html" %}
{% load static %}
{% block content %}
<div class="row justify-content-center mt-5">
<div class="col-md-6 col-lg-4">
<div class="card">
<div class="card-header">
<h3 class="text-center mb-0">Recuperar contraseña</h3>
</div>
<div class="card-body">
<form method="post" action="{% url 'reset_password_phase2' code %}">
{% csrf_token %}
<div class="mb-3">
<label for="password" class="form-label">Contraseña</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="verify_password" class="form-label">Verificar contraseña</label>
<input type="password" class="form-control" id="verify_password" name="verify_password" required>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">Recuperar contraseña</button>
</div>
<hr class="my-3">
<div class="text-center">
<p class="mb-0">¿No tienes cuenta? <a href="{% url 'register' %}">Regístrate aquí</a></p>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
+3 -1
View File
@@ -45,5 +45,7 @@ urlpatterns = [
path("usuario/direcciones/<int:id>/eliminar/", views.eliminar_direccion, name="eliminar_direccion"),
path("usuario/mensajes/", views.mensajes_comprador, name="mensajes_comprador"),
path("verify/<str:code>", views.verify, name="verify"),
path("rgpd", views.rgpd, name="rgpd")
path("rgpd", views.rgpd, name="rgpd"),
path("reset-password", views.reset_password, name="reset_password"),
path("reset-password-phase2/<str:code>", views.reset_password_phase2, name="reset_password_phase2")
]
+40 -3
View File
@@ -1,5 +1,5 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.http import Http404, HttpRequest, HttpResponse, JsonResponse
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
from django.contrib.auth.decorators import login_required
@@ -237,7 +237,7 @@ def register(request: HttpRequest):
)
tasks.enviar_correo_confirmacion.delay(user)
tasks.enviar_correo_confirmacion.delay(user.id)
messages.success(request, f"¡Cuenta creada exitosamente! Por favor, verifica tu correo entrando al Link enviado.")
return redirect("index")
@@ -1256,4 +1256,41 @@ def reset_password(request: HttpRequest):
return render(request, "tienda/reset_password", {})
def rgpd(request: HttpRequest):
return render(request, "tienda/rgpd.html", {})
return render(request, "tienda/rgpd.html", {})
def reset_password(request: HttpRequest):
if request.method == "GET":
return render(request, "tienda/reset_password.html", {})
else:
tasks.enviar_correo_recuperacion.delay(request.POST["email"])
messages.info(request, "Si tienes una cuenta con ese correo electronico, se ha enviado un correo con un enlace")
return render(request, "tienda/index.html", {})
def reset_password_phase2(request: HttpRequest, code: str):
try:
ver_code = VerificationCode.objects.get(code=code)
except VerificationCode.DoesNotExist:
raise Http404()
if ver_code.code_mode != VerificationCode.VerificationModes.RESET_PASSWORD: raise Http404()
if request.method == "GET":
return render(request, "tienda/reset_password_phase2.html", {
"code": code
})
elif request.method == "POST":
password = request.POST["password"]
vpassword = request.POST["verify_password"]
if password != vpassword:
messages.error(request, "Las contraseñas no coinciden")
return render(request, "tienda/reset_password_phase2.html", {"code": code})
user = ver_code.user
user.set_password(password)
user.save()
messages.success(request, "Se ha cambiado la contraseña!")
return redirect(reverse("index"))
else:
raise Http404()