added & configure auth login, environments & error.service

This commit is contained in:
Chneemann 2024-08-04 15:55:49 +02:00
parent 53c2caa45e
commit b8a6c95910
20 changed files with 194 additions and 38 deletions

View file

View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AuthConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'auth'

View file

@ -0,0 +1,13 @@
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
UserModel = get_user_model()
class EmailBackend(ModelBackend):
def authenticate(self, request, email=None, password=None, **kwargs):
try:
user = UserModel.objects.get(email=email)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
return None

View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,17 @@
from rest_framework import serializers
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField(required=True)
password = serializers.CharField(write_only=True, required=True)
def validate(self, data):
email = data.get('email')
password = data.get('password')
if not email:
raise serializers.ValidationError("Email is required.")
if not password:
raise serializers.ValidationError("Password is required.")
return data

View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View file

@ -0,0 +1,27 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate
from .serializer import LoginSerializer
class LoginView(APIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
email = serializer.validated_data['email']
password = serializer.validated_data['password']
user = authenticate(request, email=email, password=password)
if user:
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)

View file

@ -4,7 +4,7 @@ from .forms import CustomUserCreationForm
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
@admin.register(CustomUser) @admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin): class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm add_form = CustomUserCreationForm
fieldsets = ( fieldsets = (
@ -16,7 +16,8 @@ class CustomUserAdmin(admin.ModelAdmin):
'custom', 'custom',
'phone', 'phone',
'address', 'address',
) )
} }
) )
) )

View file

@ -1,7 +1,7 @@
from django.db import models
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser): class CustomUser(AbstractUser):
custom = models.CharField(max_length=1000, default='') custom = models.CharField(max_length=1000, blank=True)
phone = models.CharField(max_length=20, default='') phone = models.CharField(max_length=20, blank=True)
address = models.CharField(max_length=150, default='') address = models.CharField(max_length=150, blank=True)

View file

@ -1,12 +1,14 @@
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import render from django.shortcuts import render
from rest_framework.decorators import api_view from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from .serializer import UserSerializer from .serializer import UserSerializer
from .models import CustomUser from .models import CustomUser
@api_view(['GET', 'POST']) @api_view(['GET', 'POST'])
@permission_classes([IsAuthenticated])
def user_list(request): def user_list(request):
if request.method == 'GET': if request.method == 'GET':
@ -19,8 +21,10 @@ def user_list(request):
if serializer.is_valid(): if serializer.is_valid():
serializer.save serializer.save
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'DELETE']) @api_view(['GET', 'PUT', 'DELETE'])
@permission_classes([IsAuthenticated])
def user_detail(request, id): def user_detail(request, id):
try: try:
@ -35,7 +39,7 @@ def user_detail(request, id):
elif request.method == 'PUT': elif request.method == 'PUT':
serializer = UserSerializer(user, data=request.data) serializer = UserSerializer(user, data=request.data)
if serializer.is_valid(): if serializer.is_valid():
serializer().save() serializer.save()
return Response(serializer.data) return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

View file

@ -32,7 +32,6 @@ CORS_ALLOWED_ORIGINS = [
] ]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
@ -40,14 +39,12 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'rest_framework.authtoken',
'rest_framework', 'rest_framework',
'corsheaders', 'corsheaders',
'users', 'users',
] ]
AUTH_USER_MODEL = 'users.CustomUser'
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
@ -57,7 +54,6 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware'
] ]
ROOT_URLCONF = 'videoflix.urls' ROOT_URLCONF = 'videoflix.urls'
@ -80,7 +76,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'videoflix.wsgi.application' WSGI_APPLICATION = 'videoflix.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases # https://docs.djangoproject.com/en/5.0/ref/settings/#databases
@ -132,3 +127,20 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Auth
AUTH_EMAIL_VERIFICATION = True
AUTH_USER_MODEL = 'users.CustomUser'
# Rest Framework
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
AUTHENTICATION_BACKENDS = (
'auth.custom_backend.EmailBackend',
'django.contrib.auth.backends.ModelBackend',
)

View file

@ -15,11 +15,14 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path, include
from users import views from users import views
from auth.views import LoginView
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('users/', views.user_list), path('users/', views.user_list),
path('users/<int:id>/', views.user_detail), path('users/<int:id>/', views.user_detail),
path('auth/login/', LoginView.as_view()),
] ]

View file

@ -28,7 +28,9 @@
} @else { @if (mail.touched && authData.mail.length > 0 && } @else { @if (mail.touched && authData.mail.length > 0 &&
!isUserEmailValid(authData.mail.toLowerCase())) { !isUserEmailValid(authData.mail.toLowerCase())) {
<p>This is not a valid email format</p> <p>This is not a valid email format</p>
}} }} @if ((errorService.error$ | async)) {
<p>{{ errorService.error$ | async }}</p>
}
</div> </div>
<input <input
type="password" type="password"

View file

@ -1,12 +1,16 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { BtnLargeComponent } from '../../../shared/components/btn-large/btn-large.component'; import { BtnLargeComponent } from '../../../shared/components/btn-large/btn-large.component';
import { FormsModule, NgForm } from '@angular/forms'; import { FormsModule, NgForm } from '@angular/forms';
import { RouterLink } from '@angular/router'; import { Router, RouterLink } from '@angular/router';
import { AuthService } from '../../../services/auth.service';
import { ErrorService } from '../../../services/error.service';
import { HttpErrorResponse } from '@angular/common/http';
import { CommonModule } from '@angular/common';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
standalone: true, standalone: true,
imports: [BtnLargeComponent, FormsModule, RouterLink], imports: [BtnLargeComponent, CommonModule, FormsModule, RouterLink],
templateUrl: './login.component.html', templateUrl: './login.component.html',
styleUrl: './login.component.scss', styleUrl: './login.component.scss',
}) })
@ -18,17 +22,35 @@ export class LoginComponent {
send: false, send: false,
}; };
constructor() {} constructor(
private authService: AuthService,
public errorService: ErrorService,
private router: Router
) {}
isUserEmailValid(emailValue: string) { isUserEmailValid(emailValue: string) {
const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/; const emailRegex = /^[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
return emailRegex.test(emailValue); return emailRegex.test(emailValue);
} }
onSubmit(ngForm: NgForm) { async onSubmit(ngForm: NgForm) {
if (ngForm.submitted && ngForm.form.valid) { if (ngForm.submitted && ngForm.form.valid) {
console.log(this.authData); const body = {
ngForm.resetForm(); email: this.authData.mail,
password: this.authData.password,
};
try {
await this.authService.login(body);
ngForm.resetForm();
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.');
}
}
} }
} }
} }

View file

@ -0,0 +1,3 @@
export const environment = {
baseUrl: 'http://127.0.0.1:8000/',
};

View file

@ -0,0 +1,3 @@
export const environment = {
baseUrl: '',
};

View file

@ -1,12 +1,28 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import {
HttpClient,
HttpErrorResponse,
HttpHeaders,
} from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { lastValueFrom, Observable } from 'rxjs';
import { environment } from '../environments/environment.development';
import { Router } from '@angular/router';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class AuthService { export class AuthService {
constructor(private http: HttpClient) {} errorMsg: string | null = null;
constructor(private http: HttpClient, private router: Router) {}
async login(body: any): Promise<void> {
const data = await lastValueFrom(
this.http.post<any>(`${environment.baseUrl}/auth/login/`, body)
);
const authToken = data.toString();
localStorage.setItem('authToken', authToken);
}
getAllUsers(): Observable<any> { getAllUsers(): Observable<any> {
const headers = this.getAuthHeaders(); const headers = this.getAuthHeaders();

View file

@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class ErrorService {
private errorSubject = new BehaviorSubject<string | null>(null);
error$ = this.errorSubject.asObservable();
setError(message: string | null) {
this.errorSubject.next(message);
}
clearError() {
this.errorSubject.next(null);
}
}