send forgot password email
This commit is contained in:
parent
876317a994
commit
052899307e
6 changed files with 149 additions and 8 deletions
|
|
@ -59,7 +59,7 @@ class RegisterView(APIView):
|
|||
'email': user.email,
|
||||
'token': user.verify_email_token
|
||||
}
|
||||
html_body = render_to_string("mail.html", merge_data)
|
||||
html_body = render_to_string("register_mail.html", merge_data)
|
||||
|
||||
message = EmailMultiAlternatives(
|
||||
subject='Confirm your email',
|
||||
|
|
@ -89,6 +89,52 @@ class VerifyEmailView(APIView):
|
|||
else:
|
||||
return Response({"error": "Invalid email or token."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class ForgotPasswordView(APIView):
|
||||
def post(self, request):
|
||||
email = request.data.get('email')
|
||||
if not email:
|
||||
return Response({"error": "Email required."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
try:
|
||||
user = CustomUser.objects.get(email=email)
|
||||
user.verify_email_token = CustomUser.generate_verification_token()
|
||||
user.save()
|
||||
self.send_email(user)
|
||||
except CustomUser.DoesNotExist:
|
||||
pass
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
def send_email(self, user):
|
||||
merge_data = {
|
||||
'name': user.username,
|
||||
'email': user.email,
|
||||
'token': user.verify_email_token
|
||||
}
|
||||
html_body = render_to_string("forgot_password_mail.html", merge_data)
|
||||
|
||||
message = EmailMultiAlternatives(
|
||||
subject='Reset your Password',
|
||||
body=strip_tags(html_body), # Plain text fallback
|
||||
from_email='noreply@videoflix.com',
|
||||
to=['andre.kempf.dev@gmail.com']
|
||||
)
|
||||
message.attach_alternative(html_body, "text/html")
|
||||
message.send(fail_silently=False)
|
||||
|
||||
class ChangePasswordView(APIView):
|
||||
def post(self, request):
|
||||
email = request.data.get('email')
|
||||
token = request.data.get('token')
|
||||
|
||||
if not email or not token:
|
||||
return Response({"error": "Email and token are required."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = CustomUser.objects.filter(Q(email=email) & Q(verify_email_token=token)).first()
|
||||
if user:
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
else:
|
||||
return Response({"error": "Invalid email or token."}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class AuthView(ObtainAuthToken):
|
||||
authentication_classes = [authentication.TokenAuthentication]
|
||||
|
||||
|
|
|
|||
48
backend/videoflix/templates/forgot_password_mail.html
Normal file
48
backend/videoflix/templates/forgot_password_mail.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; }
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 9px 18px;
|
||||
width: fit-content;
|
||||
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 24px;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
background-color: #2e3edf;
|
||||
border: 1px solid #2e3edf;
|
||||
transition: 300ms ease-in-out;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn:hover {
|
||||
background-color: #fff;
|
||||
color: #2e3edf;
|
||||
border: 1px solid #2e3edf;
|
||||
transition: 300ms ease-in-out;
|
||||
}
|
||||
.center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
img {
|
||||
width: 249px;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="center"><img src="https://andre-kempf.com/videoflix-logo.png" alt=""></div>
|
||||
<p>Dear {{ name }},</p>
|
||||
<p>
|
||||
We recently received a request to reset your password. If you made this request, please click on the following link to reset your password:
|
||||
</p>
|
||||
<a class="btn" href="http://localhost:4200/forgot-password?email={{ email }}&token={{ token }}">Reset Password</a>
|
||||
<p>Please note that for security reasons, this link is only valid for 24 hours.</p>
|
||||
<p>If you did not request a password reset, please ignore this email.</p>
|
||||
<p>Best regards,</p>
|
||||
<p>Your Videoflix team!</p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -17,7 +17,7 @@ Including another URLconf
|
|||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from users import views
|
||||
from auth.views import LoginView, RegisterView, VerifyEmailView, AuthView
|
||||
from auth.views import LoginView, RegisterView, VerifyEmailView, AuthView, ForgotPasswordView
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
|
|
@ -27,5 +27,6 @@ urlpatterns = [
|
|||
path('auth/login/', LoginView.as_view()),
|
||||
path('auth/register/', RegisterView.as_view()),
|
||||
path('auth/verify-email/', VerifyEmailView.as_view()),
|
||||
path('auth/forgot-password/', ForgotPasswordView.as_view()),
|
||||
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ import { Component } from '@angular/core';
|
|||
import { BtnLargeComponent } from '../../../shared/components/btn-large/btn-large.component';
|
||||
import { FormsModule, NgForm } from '@angular/forms';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { AuthService } from '../../../services/auth.service';
|
||||
import { ErrorService } from '../../../services/error.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-forgot-password',
|
||||
|
|
@ -14,6 +17,7 @@ import { ActivatedRoute, RouterLink } from '@angular/router';
|
|||
export class ForgotPasswordComponent {
|
||||
authData = {
|
||||
mail: '',
|
||||
token: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
};
|
||||
|
|
@ -22,13 +26,21 @@ export class ForgotPasswordComponent {
|
|||
queryEmail: boolean = false;
|
||||
queryEmailSuccess: boolean = false;
|
||||
|
||||
constructor(private route: ActivatedRoute) {}
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private authService: AuthService,
|
||||
public errorService: ErrorService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.queryParams.subscribe((params) => {
|
||||
this.authData.mail = params['mail'] || '';
|
||||
this.authData.mail = params['email'] || '';
|
||||
this.authData.token = params['token'] || '';
|
||||
this.queryEmailSuccess = params['pw-change'] || '';
|
||||
});
|
||||
if (this.authData.mail && this.authData.token) {
|
||||
this.queryEmail = true;
|
||||
}
|
||||
}
|
||||
|
||||
isUserEmailValid(emailValue: string) {
|
||||
|
|
@ -39,16 +51,44 @@ export class ForgotPasswordComponent {
|
|||
onSubmit(ngForm: NgForm, mailInput: any) {
|
||||
if (ngForm.submitted && ngForm.form.valid) {
|
||||
if (mailInput.name === 'mail') {
|
||||
try {
|
||||
this.verifyEmail();
|
||||
ngForm.form.reset();
|
||||
this.sendMailSuccess = true;
|
||||
} catch {}
|
||||
} else if (mailInput.name === 'password') {
|
||||
ngForm.form.reset();
|
||||
this.queryEmail = false;
|
||||
this.queryEmailSuccess = true;
|
||||
}
|
||||
console.log(this.authData);
|
||||
} else {
|
||||
mailInput.control.markAsTouched();
|
||||
}
|
||||
}
|
||||
|
||||
async verifyEmail() {
|
||||
const body = {
|
||||
email: this.authData.mail,
|
||||
};
|
||||
try {
|
||||
await this.authService.forgotPassword(body);
|
||||
this.sendMailSuccess = true;
|
||||
this.errorService.clearError();
|
||||
} catch (error) {
|
||||
this.sendMailSuccess = false;
|
||||
this.errorMsg(error);
|
||||
}
|
||||
}
|
||||
|
||||
errorMsg(error: any) {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
const errorTypes = ['error'];
|
||||
for (const type of errorTypes) {
|
||||
if (error.error[type]) {
|
||||
this.errorService.setError(type, error.error[type]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.errorService.clearError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ export class AuthService {
|
|||
);
|
||||
}
|
||||
|
||||
async forgotPassword(body: any) {
|
||||
await lastValueFrom(
|
||||
this.http.post(`${environment.baseUrl}/auth/forgot-password/`, body)
|
||||
);
|
||||
}
|
||||
|
||||
storeAuthToken(data: any, storage: boolean) {
|
||||
storage
|
||||
? localStorage.setItem('authToken', data.toString())
|
||||
|
|
|
|||
Loading…
Reference in a new issue