Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
20e0805d0e |
|||
|
0bab8a343c | ||
|
e7e43839eb | ||
|
b4351ceaea | ||
46e52e20cc |
|||
48119d1faf |
5 changed files with 99 additions and 46 deletions
|
@ -3,6 +3,10 @@ name: CI
|
|||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
changed_files:
|
||||
name: Get Changed Files
|
||||
|
|
|
@ -2,6 +2,7 @@ package de.szut.casino.exceptionHandling;
|
|||
|
||||
import de.szut.casino.exceptionHandling.exceptions.InsufficientFundsException;
|
||||
import de.szut.casino.exceptionHandling.exceptions.UserNotFoundException;
|
||||
import jakarta.persistence.EntityExistsException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
|
@ -24,4 +25,10 @@ public class GlobalExceptionHandler {
|
|||
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
|
||||
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(EntityExistsException.class)
|
||||
public ResponseEntity<?> handleEntityExistsException(EntityExistsException ex, WebRequest request) {
|
||||
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
|
||||
return new ResponseEntity<>(errorDetails, HttpStatus.CONFLICT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package de.szut.casino.user;
|
||||
|
||||
import de.szut.casino.user.dto.CreateUserDto;
|
||||
import jakarta.persistence.EntityExistsException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
@ -19,11 +20,11 @@ public class UserService {
|
|||
|
||||
public UserEntity createUser(CreateUserDto createUserDto) {
|
||||
if (userRepository.existsByUsername(createUserDto.getUsername())) {
|
||||
throw new IllegalArgumentException("Username is already taken");
|
||||
throw new EntityExistsException("Username is already taken");
|
||||
}
|
||||
|
||||
if (userRepository.existsByEmail(createUserDto.getEmail())) {
|
||||
throw new IllegalArgumentException("Email is already in use");
|
||||
throw new EntityExistsException("Email is already in use");
|
||||
}
|
||||
|
||||
UserEntity user = new UserEntity(
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
<div class="modal-card max-w-md w-full">
|
||||
<h2 class="modal-heading text-center">Konto erstellen</h2>
|
||||
|
||||
<div *ngIf="errorMessage" class="bg-accent-red text-white p-4 rounded mb-4">
|
||||
{{ errorMessage }}
|
||||
@if (errorMessage()) {
|
||||
<div class="bg-accent-red text-white p-4 rounded mb-4">
|
||||
{{ errorMessage() }}
|
||||
</div>
|
||||
}
|
||||
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="space-y-4">
|
||||
<div>
|
||||
|
@ -14,18 +16,26 @@
|
|||
type="email"
|
||||
formControlName="email"
|
||||
class="w-full px-4 py-2.5 bg-deep-blue-light/50 text-white rounded-lg my-1 border border-deep-blue-light/30 focus:border-emerald/50 focus:ring-1 focus:ring-emerald/50 outline-none transition-all duration-200"
|
||||
[ngClass]="{ 'border-accent-red': fieldErrors()['email'] }"
|
||||
placeholder="Gib deine E-Mail-Adresse ein"
|
||||
/>
|
||||
|
||||
<div
|
||||
*ngIf="form['email'].touched && form['email'].errors"
|
||||
class="text-accent-red mt-1 text-sm"
|
||||
>
|
||||
<span *ngIf="form['email'].errors?.['required']">E-Mail ist erforderlich</span>
|
||||
<span *ngIf="form['email'].errors?.['email']">
|
||||
Bitte gib eine gültige E-Mail-Adresse ein
|
||||
</span>
|
||||
@if (fieldErrors()['email']) {
|
||||
<div class="text-accent-red mt-1 text-sm">
|
||||
{{ fieldErrors()['email'] }}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!fieldErrors()['email'] && form['email'].touched && form['email'].errors) {
|
||||
<div class="text-accent-red mt-1 text-sm">
|
||||
@if (form['email'].errors['required']) {
|
||||
<span>E-Mail ist erforderlich</span>
|
||||
}
|
||||
@if (form['email'].errors['email']) {
|
||||
<span>Bitte gib eine gültige E-Mail-Adresse ein</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -37,18 +47,26 @@
|
|||
type="text"
|
||||
formControlName="username"
|
||||
class="w-full px-4 py-2.5 bg-deep-blue-light/50 text-white rounded-lg my-1 border border-deep-blue-light/30 focus:border-emerald/50 focus:ring-1 focus:ring-emerald/50 outline-none transition-all duration-200"
|
||||
[ngClass]="{ 'border-accent-red': fieldErrors()['username'] }"
|
||||
placeholder="Wähle einen Benutzernamen"
|
||||
/>
|
||||
|
||||
<div
|
||||
*ngIf="form['username'].touched && form['username'].errors"
|
||||
class="text-accent-red mt-1 text-sm"
|
||||
>
|
||||
<span *ngIf="form['username'].errors?.['required']">Benutzername ist erforderlich</span>
|
||||
<span *ngIf="form['username'].errors?.['minlength']">
|
||||
Benutzername muss mindestens 3 Zeichen haben
|
||||
</span>
|
||||
@if (fieldErrors()['username']) {
|
||||
<div class="text-accent-red mt-1 text-sm">
|
||||
{{ fieldErrors()['username'] }}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (!fieldErrors()['username'] && form['username'].touched && form['username'].errors) {
|
||||
<div class="text-accent-red mt-1 text-sm">
|
||||
@if (form['username'].errors['required']) {
|
||||
<span>Benutzername ist erforderlich</span>
|
||||
}
|
||||
@if (form['username'].errors['minlength']) {
|
||||
<span>Benutzername muss mindestens 3 Zeichen haben</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
@ -63,24 +81,25 @@
|
|||
placeholder="Erstelle ein Passwort"
|
||||
/>
|
||||
|
||||
<div
|
||||
*ngIf="form['password'].touched && form['password'].errors"
|
||||
class="text-accent-red mt-1 text-sm"
|
||||
>
|
||||
<span *ngIf="form['password'].errors?.['required']">Passwort ist erforderlich</span>
|
||||
<span *ngIf="form['password'].errors?.['minlength']">
|
||||
Passwort muss mindestens 6 Zeichen haben
|
||||
</span>
|
||||
@if (form['password'].touched && form['password'].errors) {
|
||||
<div class="text-accent-red mt-1 text-sm">
|
||||
@if (form['password'].errors['required']) {
|
||||
<span>Passwort ist erforderlich</span>
|
||||
}
|
||||
@if (form['password'].errors['minlength']) {
|
||||
<span>Passwort muss mindestens 6 Zeichen haben</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="pt-2">
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="registerForm.invalid || isLoading"
|
||||
[disabled]="registerForm.invalid || isLoading()"
|
||||
class="button-primary w-full py-2.5 rounded"
|
||||
>
|
||||
{{ isLoading ? 'Konto wird erstellt...' : 'Registrieren' }}
|
||||
{{ isLoading() ? 'Konto wird erstellt...' : 'Registrieren' }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component, signal } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { RegisterRequest } from '../../../model/auth/RegisterRequest';
|
||||
import { AuthService } from '@service/auth.service';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-register',
|
||||
|
@ -13,8 +14,9 @@ import { CommonModule } from '@angular/common';
|
|||
})
|
||||
export class RegisterComponent {
|
||||
registerForm: FormGroup;
|
||||
errorMessage = '';
|
||||
isLoading = false;
|
||||
errorMessage = signal<string>('');
|
||||
isLoading = signal<boolean>(false);
|
||||
fieldErrors = signal<Record<string, string>>({});
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
|
@ -37,8 +39,9 @@ export class RegisterComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
this.errorMessage = '';
|
||||
this.isLoading.set(true);
|
||||
this.errorMessage.set('');
|
||||
this.fieldErrors.set({});
|
||||
|
||||
const registerRequest: RegisterRequest = {
|
||||
email: this.form['email'].value,
|
||||
|
@ -48,7 +51,6 @@ export class RegisterComponent {
|
|||
|
||||
this.authService.register(registerRequest).subscribe({
|
||||
next: () => {
|
||||
// After registration, log in the user
|
||||
this.authService
|
||||
.login({
|
||||
usernameOrEmail: registerRequest.email,
|
||||
|
@ -59,15 +61,35 @@ export class RegisterComponent {
|
|||
this.router.navigate(['/home']);
|
||||
},
|
||||
error: () => {
|
||||
this.isLoading = false;
|
||||
this.errorMessage =
|
||||
'Registration successful but failed to login automatically. Please log in manually.';
|
||||
this.isLoading.set(false);
|
||||
this.errorMessage.set(
|
||||
'Registration successful but failed to login automatically. Please log in manually.'
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
error: (err) => {
|
||||
this.isLoading = false;
|
||||
this.errorMessage = err.error?.message || 'Failed to register. Please try again.';
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this.isLoading.set(false);
|
||||
|
||||
if (err.status === 409) {
|
||||
const message = err.error?.message;
|
||||
switch (message) {
|
||||
case 'Email is already in use':
|
||||
this.fieldErrors.update((errors) => ({
|
||||
...errors,
|
||||
email: 'Diese E-Mail-Adresse wird bereits verwendet.',
|
||||
}));
|
||||
break;
|
||||
case 'Username is already taken':
|
||||
this.fieldErrors.update((errors) => ({
|
||||
...errors,
|
||||
username: 'Dieser Benutzername ist bereits vergeben.',
|
||||
}));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.errorMessage.set(err.error?.message || 'Failed to register. Please try again.');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Reference in a new issue