From 133735e2f7a0474150c027c3b566881a90201506 Mon Sep 17 00:00:00 2001
From: Chneemann
Date: Mon, 5 Aug 2024 18:02:51 +0200
Subject: [PATCH] user is now unlocked by his email and token from the email
link
---
backend/videoflix/auth/views.py | 20 +++++-
backend/videoflix/templates/mail.html | 2 +-
backend/videoflix/users/admin.py | 2 +-
backend/videoflix/users/models.py | 12 ++--
frontend/src/app/app.routes.ts | 1 +
.../verify-email/verify-email.component.html | 21 ++++++
.../verify-email/verify-email.component.scss | 8 +++
.../verify-email.component.spec.ts | 23 +++++++
.../verify-email/verify-email.component.ts | 64 +++++++++++++++++++
.../app/components/home/home.component.html | 1 +
.../src/app/components/home/home.component.ts | 2 +
frontend/src/app/services/auth.service.ts | 14 ++--
12 files changed, 156 insertions(+), 14 deletions(-)
create mode 100644 frontend/src/app/components/auth/verify-email/verify-email.component.html
create mode 100644 frontend/src/app/components/auth/verify-email/verify-email.component.scss
create mode 100644 frontend/src/app/components/auth/verify-email/verify-email.component.spec.ts
create mode 100644 frontend/src/app/components/auth/verify-email/verify-email.component.ts
diff --git a/backend/videoflix/auth/views.py b/backend/videoflix/auth/views.py
index a55c365..f80407b 100644
--- a/backend/videoflix/auth/views.py
+++ b/backend/videoflix/auth/views.py
@@ -9,6 +9,7 @@ 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
+from django.db.models import Q
class LoginView(APIView):
serializer_class = LoginSerializer
@@ -55,7 +56,7 @@ class RegisterView(APIView):
merge_data = {
'name': user.username,
'email': user.email,
- 'token': user.verify_email
+ 'token': user.verify_email_token
}
html_body = render_to_string("mail.html", merge_data)
@@ -70,4 +71,19 @@ class RegisterView(APIView):
class VerifyEmailView(APIView):
def post(self, request):
- pass
\ No newline at end of file
+ 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:
+ if user.is_active:
+ return Response({"error": "User has already been activated"}, status=status.HTTP_409_CONFLICT)
+
+ user.is_active = True
+ user.save()
+ return Response(status=status.HTTP_200_OK)
+ else:
+ return Response({"error": "Invalid email or token."}, status=status.HTTP_400_BAD_REQUEST)
diff --git a/backend/videoflix/templates/mail.html b/backend/videoflix/templates/mail.html
index a4ab908..240233d 100644
--- a/backend/videoflix/templates/mail.html
+++ b/backend/videoflix/templates/mail.html
@@ -40,7 +40,7 @@
Thank you for registering with Videoflix. To complete your registration
and verify your email address, please click the link below:
- Activate account
+ Activate account
If you did not create an account with us, please disregard this email.
diff --git a/backend/videoflix/users/admin.py b/backend/videoflix/users/admin.py
index 12ef327..d228978 100644
--- a/backend/videoflix/users/admin.py
+++ b/backend/videoflix/users/admin.py
@@ -13,7 +13,7 @@ class CustomUserAdmin(UserAdmin):
'Individual data',
{
'fields': (
- 'verify_email',
+ 'verify_email_token',
)
}
)
diff --git a/backend/videoflix/users/models.py b/backend/videoflix/users/models.py
index 5378cdc..b8a922d 100644
--- a/backend/videoflix/users/models.py
+++ b/backend/videoflix/users/models.py
@@ -1,17 +1,17 @@
-import secrets
import string
-from django.db import models
+import secrets
from django.contrib.auth.models import AbstractUser
+from django.db import models
class CustomUser(AbstractUser):
- verify_email = models.CharField(max_length=15, blank=True)
+ verify_email_token = models.CharField(max_length=20, blank=True, null=True)
def save(self, *args, **kwargs):
- if not self.verify_email:
- self.verify_email = self.generate_verification_token()
+ if not self.verify_email_token:
+ self.verify_email_token = self.generate_verification_token()
super().save(*args, **kwargs)
@staticmethod
- def generate_verification_token(length=15):
+ def generate_verification_token(length=20):
characters = string.ascii_letters + string.digits
return ''.join(secrets.choice(characters) for _ in range(length))
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index ccf07ff..1ed769b 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -9,6 +9,7 @@ export const routes: Routes = [
{ path: 'login', component: HomeComponent },
{ path: 'register', component: HomeComponent },
{ path: 'forgot-password', component: HomeComponent },
+ { path: 'verify-email', component: HomeComponent },
{ path: 'imprint', component: ImprintComponent },
{ path: 'privacy-policy', component: PrivacyPolicyComponent },
{ path: 'browse', component: BrowseComponent },
diff --git a/frontend/src/app/components/auth/verify-email/verify-email.component.html b/frontend/src/app/components/auth/verify-email/verify-email.component.html
new file mode 100644
index 0000000..5de9b15
--- /dev/null
+++ b/frontend/src/app/components/auth/verify-email/verify-email.component.html
@@ -0,0 +1,21 @@
+
+
+
Verify Email
+
+ @if (verified) {
+
Your email has been successfully verified! You can now log in.
+
To the login
+ } @else {
+
+ There was an issue verifying your email.
Please try again or
+ contact support for assistance.
+
+ }
+
+
+
+
{{ errors["error"] }}
+
+
+
+
diff --git a/frontend/src/app/components/auth/verify-email/verify-email.component.scss b/frontend/src/app/components/auth/verify-email/verify-email.component.scss
new file mode 100644
index 0000000..bbc955c
--- /dev/null
+++ b/frontend/src/app/components/auth/verify-email/verify-email.component.scss
@@ -0,0 +1,8 @@
+@import "./../../../../assets/style/colors.scss";
+@import "./../../../../assets/style/form.scss";
+@import "./../../../../assets/style/auth-layout.scss";
+
+section {
+ background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)),
+ url(./../../../../assets/img/backgrounds/login.png);
+}
diff --git a/frontend/src/app/components/auth/verify-email/verify-email.component.spec.ts b/frontend/src/app/components/auth/verify-email/verify-email.component.spec.ts
new file mode 100644
index 0000000..1946524
--- /dev/null
+++ b/frontend/src/app/components/auth/verify-email/verify-email.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { VerifyEmailComponent } from './verify-email.component';
+
+describe('VerifyEmailComponent', () => {
+ let component: VerifyEmailComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [VerifyEmailComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(VerifyEmailComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/components/auth/verify-email/verify-email.component.ts b/frontend/src/app/components/auth/verify-email/verify-email.component.ts
new file mode 100644
index 0000000..460edcc
--- /dev/null
+++ b/frontend/src/app/components/auth/verify-email/verify-email.component.ts
@@ -0,0 +1,64 @@
+import { Component } from '@angular/core';
+import { ActivatedRoute, RouterLink } from '@angular/router';
+import { AuthService } from '../../../services/auth.service';
+import { ErrorService } from '../../../services/error.service';
+import { CommonModule } from '@angular/common';
+import { HttpErrorResponse } from '@angular/common/http';
+
+@Component({
+ selector: 'app-verify-email',
+ standalone: true,
+ imports: [CommonModule, RouterLink],
+ templateUrl: './verify-email.component.html',
+ styleUrl: './verify-email.component.scss',
+})
+export class VerifyEmailComponent {
+ verified: boolean = false;
+
+ authData = {
+ mail: '',
+ token: '',
+ };
+
+ constructor(
+ private route: ActivatedRoute,
+ private authService: AuthService,
+ public errorService: ErrorService
+ ) {}
+
+ ngOnInit(): void {
+ this.route.queryParams.subscribe((params) => {
+ this.authData.mail = params['email'] || '';
+ this.authData.token = params['token'] || '';
+ this.verifyEmail();
+ });
+ }
+
+ async verifyEmail() {
+ const body = {
+ email: this.authData.mail,
+ token: this.authData.token,
+ };
+ try {
+ await this.authService.verifyEmail(body);
+ this.verified = true;
+ this.errorService.clearError();
+ } catch (error) {
+ this.verified = 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();
+ }
+ }
+}
diff --git a/frontend/src/app/components/home/home.component.html b/frontend/src/app/components/home/home.component.html
index dbd88c9..fc24b03 100644
--- a/frontend/src/app/components/home/home.component.html
+++ b/frontend/src/app/components/home/home.component.html
@@ -6,5 +6,6 @@
+
diff --git a/frontend/src/app/components/home/home.component.ts b/frontend/src/app/components/home/home.component.ts
index 2eb7ec1..1921979 100644
--- a/frontend/src/app/components/home/home.component.ts
+++ b/frontend/src/app/components/home/home.component.ts
@@ -8,6 +8,7 @@ import { CommonModule } from '@angular/common';
import { LoginComponent } from '../auth/login/login.component';
import { ForgotPasswordComponent } from '../auth/forgot-password/forgot-password.component';
import { BrowseComponent } from './browse/browse.component';
+import { VerifyEmailComponent } from '../auth/verify-email/verify-email.component';
@Component({
selector: 'app-home',
@@ -20,6 +21,7 @@ import { BrowseComponent } from './browse/browse.component';
RegisterComponent,
LoginComponent,
ForgotPasswordComponent,
+ VerifyEmailComponent,
BrowseComponent,
],
templateUrl: './home.component.html',
diff --git a/frontend/src/app/services/auth.service.ts b/frontend/src/app/services/auth.service.ts
index 669facb..062abb3 100644
--- a/frontend/src/app/services/auth.service.ts
+++ b/frontend/src/app/services/auth.service.ts
@@ -11,22 +11,28 @@ export class AuthService {
constructor(private http: HttpClient) {}
- async login(body: any, storage: boolean): Promise {
+ async login(body: any, storage: boolean) {
const data = await lastValueFrom(
- this.http.post(`${environment.baseUrl}/auth/login/`, body)
+ this.http.post(`${environment.baseUrl}/auth/login/`, body)
);
this.storeAuthToken(data, storage);
}
+ async verifyEmail(body: any) {
+ await lastValueFrom(
+ this.http.post(`${environment.baseUrl}/auth/verify-email/`, body)
+ );
+ }
+
storeAuthToken(data: any, storage: boolean) {
storage
? localStorage.setItem('authToken', data.toString())
: sessionStorage.setItem('authToken', data.toString());
}
- async register(body: any): Promise {
+ async register(body: any) {
await lastValueFrom(
- this.http.post(`${environment.baseUrl}/auth/register/`, body)
+ this.http.post(`${environment.baseUrl}/auth/register/`, body)
);
}
}