update error.service, added register send email for activation

This commit is contained in:
Chneemann 2024-08-05 00:05:26 +02:00
parent 1d1e6e58d9
commit 537519a3dd
9 changed files with 161 additions and 24 deletions

View file

@ -4,6 +4,11 @@ from rest_framework import status
from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate
from .serializer import LoginSerializer
from users.serializer import UserSerializer
from django.utils.html import strip_tags
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from users.models import CustomUser
class LoginView(APIView):
serializer_class = LoginSerializer
@ -17,11 +22,46 @@ class LoginView(APIView):
user = authenticate(request, email=email, password=password)
if user:
if not user.is_active:
return Response({'password': 'Account is inactive, please check your mails'}, status=status.HTTP_403_FORBIDDEN)
return self._create_token_response(user)
return Response({'detail': 'Unable to login with provided credentials.'}, status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def _create_token_response(self, user):
token, created = Token.objects.get_or_create(user=user)
return Response({token.key}, status=status.HTTP_200_OK)
token = Token.objects.get_or_create(user=user)
return Response({'token': token.key}, status=status.HTTP_200_OK)
class RegisterView(APIView):
def post(self, request):
email = request.data.get('email')
password = request.data.get('password')
if CustomUser.objects.filter(email=email).exists():
return Response({'detail': 'A user with this email already exists.'}, status=status.HTTP_400_BAD_REQUEST)
serialized = UserSerializer(data=request.data)
if serialized.is_valid():
user = serialized.save()
user.is_active = False
user.set_password(password)
user.save()
self.send_email(user)
return Response(serialized.data, status=status.HTTP_201_CREATED)
else:
return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)
def send_email(self, user):
merge_data = {
'name': user.username
}
html_body = render_to_string("mail.html", merge_data)
message = EmailMultiAlternatives(
subject='Confirm your email',
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)

View file

@ -0,0 +1,50 @@
<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>
Thank you for registering with Videoflix. To complete your registration
and verify your email address, please click the link below:
</p>
<a class="btn" href="">Activate account</a>
<p>
If you did not create an account with us, please disregard this email.
</p>
<p>Best regards,</p>
<p>Your Videoflix Team.</p>
</body>
</html>

View file

@ -10,6 +10,7 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
@ -25,6 +26,14 @@ SECRET_KEY = 'django-insecure-*o%q)*3l+9^fs!m0z_h=z9bc)su=%)fv$@ktb#pza-pvi^z*en
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
# EMAIL
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'andre.kempf.dev@gmail.com'
EMAIL_HOST_PASSWORD = 'oyxwawshudwytgud'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
ALLOWED_HOSTS = []
CORS_ALLOWED_ORIGINS = [
@ -61,7 +70,7 @@ ROOT_URLCONF = 'videoflix.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [

View file

@ -17,12 +17,12 @@ Including another URLconf
from django.contrib import admin
from django.urls import path, include
from users import views
from auth.views import LoginView
from auth.views import LoginView, RegisterView
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', views.user_list),
path('users/<int:id>/', views.user_detail),
path('auth/login/', LoginView.as_view()),
path('auth/register/', RegisterView.as_view()),
]

View file

@ -5,6 +5,7 @@
#loginForm="ngForm"
(ngSubmit)="onSubmit(loginForm)"
onsubmit="return false"
(input)="this.errorService.clearError()"
>
<input
type="mail"
@ -28,9 +29,13 @@
} @else { @if (mail.touched && authData.mail.length > 0 &&
!isUserEmailValid(authData.mail.toLowerCase())) {
<p>This is not a valid email format</p>
}} @if ((errorService.error$ | async)) {
<p>{{ errorService.error$ | async }}</p>
}
}}
<!-- Backend error handling -->
<div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['mail']">
<p>{{ errors["mail"] }}</p>
</div>
</div>
</div>
<input
type="password"
@ -55,6 +60,12 @@
password.touched) {
<p>Password is too short, min 6 characters</p>
} }
<!-- Backend error handling -->
<div *ngIf="errorService.error$ | async as errors">
<div *ngIf="errors?.['password']">
<p>{{ errors["password"] }}</p>
</div>
</div>
</div>
<div class="checkbox">
<label class="container"

View file

@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, ViewChild } from '@angular/core';
import { BtnLargeComponent } from '../../../shared/components/btn-large/btn-large.component';
import { FormsModule, NgForm } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
@ -45,12 +45,21 @@ export class LoginComponent {
this.router.navigate(['/browse/']);
this.errorService.clearError();
} catch (error) {
if (error instanceof HttpErrorResponse) {
this.errorService.setError(error.error.detail);
} else {
this.errorService.setError('An unknown error has occurred.');
}
this.errorMsg(error);
}
}
}
errorMsg(error: any) {
if (error instanceof HttpErrorResponse) {
const errorTypes = ['mail', 'password'];
for (const type of errorTypes) {
if (error.error[type]) {
this.errorService.setError(type, error.error[type]);
return;
}
}
this.errorService.clearError();
}
}
}

View file

@ -14,6 +14,7 @@
#registerForm="ngForm"
(ngSubmit)="onSubmit(registerForm)"
onsubmit="return false"
(input)="this.errorService.clearError()"
>
<input
type="mail"

View file

@ -55,11 +55,11 @@ export class RegisterComponent {
this.registrationSuccess = true;
this.errorService.clearError();
} catch (error) {
if (error instanceof HttpErrorResponse) {
this.errorService.setError(error.error.detail);
} else {
this.errorService.setError('An unknown error has occurred.');
}
// if (error instanceof HttpErrorResponse) {
// this.errorService.setError(error.error.detail);
// } else {
// this.errorService.setError('An unknown error has occurred.');
// }
}
}
}

View file

@ -1,15 +1,32 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface ErrorDetail {
type: string;
message: string;
}
@Injectable({
providedIn: 'root',
})
export class ErrorService {
private errorSubject = new BehaviorSubject<string | null>(null);
error$ = this.errorSubject.asObservable();
private errorSubject = new BehaviorSubject<ErrorDetail | null>(null);
setError(message: string | null) {
this.errorSubject.next(message);
error$: Observable<{ [key: string]: string } | null> = this.errorSubject
.asObservable()
.pipe(
map((errorDetail) => {
if (errorDetail) {
return { [errorDetail.type]: errorDetail.message };
}
return null;
})
);
setError(type: string, message: string) {
const errorDetail: ErrorDetail = { type, message };
this.errorSubject.next(errorDetail);
}
clearError() {