From 793f3f68345c8233511beb7b7fa10a46ae40ecd3 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Thu, 13 Feb 2025 13:16:59 +0100 Subject: [PATCH 01/11] feat: add user creation on login (wip) --- .../de/szut/casino/user/UserController.java | 25 +++++++++++++ .../java/de/szut/casino/user/UserEntity.java | 18 +++++++++ .../de/szut/casino/user/UserRepository.java | 16 ++++++++ .../java/de/szut/casino/user/UserService.java | 37 +++++++++++++++++++ .../szut/casino/user/dto/CreateUserDto.java | 15 ++++++++ .../de/szut/casino/user/dto/GetUserDto.java | 15 ++++++++ frontend/src/app/app.config.ts | 2 +- frontend/src/app/app.routes.ts | 5 +++ frontend/src/app/auth.guard.ts | 12 ++---- .../login-success/login-success.component.css | 0 .../login-success.component.html | 1 + .../login-success/login-success.component.ts | 25 +++++++++++++ frontend/src/app/service/user.service.ts | 34 +++++++++++++++++ 13 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/de/szut/casino/user/UserController.java create mode 100644 backend/src/main/java/de/szut/casino/user/UserEntity.java create mode 100644 backend/src/main/java/de/szut/casino/user/UserRepository.java create mode 100644 backend/src/main/java/de/szut/casino/user/UserService.java create mode 100644 backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java create mode 100644 backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java create mode 100644 frontend/src/app/login-success/login-success.component.css create mode 100644 frontend/src/app/login-success/login-success.component.html create mode 100644 frontend/src/app/login-success/login-success.component.ts create mode 100644 frontend/src/app/service/user.service.ts diff --git a/backend/src/main/java/de/szut/casino/user/UserController.java b/backend/src/main/java/de/szut/casino/user/UserController.java new file mode 100644 index 0000000..7ee0739 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/UserController.java @@ -0,0 +1,25 @@ +package de.szut.casino.user; + +import de.szut.casino.user.dto.CreateUserDto; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +public class UserController { + + @Autowired + private UserService userService; + + + @GetMapping("/user/{id}") + public ResponseEntity getUser(@PathVariable String id) { + return ResponseEntity.ok(userService.getUser(id)); + } + + @PostMapping("/user") + public ResponseEntity createUser(@RequestBody @Valid CreateUserDto userData) { + return ResponseEntity.ok(userService.createUser(userData)); + } +} diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java new file mode 100644 index 0000000..3fb45a2 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java @@ -0,0 +1,18 @@ +package de.szut.casino.user; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Entity +public class UserEntity { + @Id + @GeneratedValue + private Long id; + private String keycloakId; + private String username; +} diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java new file mode 100644 index 0000000..f009b44 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java @@ -0,0 +1,16 @@ +package de.szut.casino.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public interface UserRepository extends JpaRepository { + boolean existsByUsername(String username); + + @Query("SELECT u FROM UserEntity u WHERE u.keycloakId = ?1") + Optional findOneByKeycloakId(String keycloakId); + +} diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java new file mode 100644 index 0000000..95cb01a --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -0,0 +1,37 @@ +package de.szut.casino.user; + +import de.szut.casino.user.dto.CreateUserDto; +import de.szut.casino.user.dto.GetUserDto; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +public class UserService { + @Autowired + private UserRepository userRepository; + + public UserEntity createUser(CreateUserDto createUserDto) { + UserEntity user = new UserEntity(); + user.setUsername(createUserDto.getUsername()); + user.setKeycloakId(createUserDto.getKeycloakId()); + userRepository.save(user); + + return user; + } + + public GetUserDto getUser(String keycloakId) { + Optional user = this.userRepository.findOneByKeycloakId(keycloakId); + if (user.isPresent()) { + System.out.println(user.get()); + GetUserDto getUserDto = new GetUserDto(); + getUserDto.setKeycloakId(user.get().getKeycloakId()); + getUserDto.setUsername(user.get().getUsername()); + return getUserDto; + } + System.out.println("User not found: "+keycloakId); + + return null; + } +} diff --git a/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java new file mode 100644 index 0000000..ff28427 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java @@ -0,0 +1,15 @@ +package de.szut.casino.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CreateUserDto { + private String keycloakId; + private String username; +} diff --git a/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java new file mode 100644 index 0000000..95855f1 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java @@ -0,0 +1,15 @@ +package de.szut.casino.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class GetUserDto { + private String keycloakId; + private String username; +} diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 8401ece..cbe2fcf 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -27,7 +27,7 @@ export const initializeKeycloak = (keycloak: KeycloakService) => async () => onLoad: 'check-sso', silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', checkLoginIframe: false, - redirectUri: 'http://localhost:4200', + redirectUri: window.location.origin + '/login/success', }, }); diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 73ed20c..bbf1a56 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -2,12 +2,17 @@ import { Routes } from '@angular/router'; import { LandingComponent } from './feature/landing/landing.component'; import { HomeComponent } from './feature/home/home.component'; import { authGuard } from './auth.guard'; +import { LoginSuccessComponent } from './login-success/login-success.component'; export const routes: Routes = [ { path: '', component: LandingComponent, }, + { + path: 'login/success', + component: LoginSuccessComponent, + }, { path: 'home', component: HomeComponent, diff --git a/frontend/src/app/auth.guard.ts b/frontend/src/app/auth.guard.ts index 0743ff6..e97e4fc 100644 --- a/frontend/src/app/auth.guard.ts +++ b/frontend/src/app/auth.guard.ts @@ -1,4 +1,4 @@ -import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router'; import { inject } from '@angular/core'; import { KeycloakService } from 'keycloak-angular'; @@ -7,17 +7,13 @@ export const authGuard: CanActivateFn = async ( state: RouterStateSnapshot ) => { const keycloakService = inject(KeycloakService); - const isLoggedIn = keycloakService.isLoggedIn(); + const router = inject(Router); - if (isLoggedIn) { + if (keycloakService.isLoggedIn()) { return true; } - const baseurl = window.location.origin; - - keycloakService.login({ - redirectUri: `${baseurl}${state.url}`, - }); + router.navigate(['']); return false; }; diff --git a/frontend/src/app/login-success/login-success.component.css b/frontend/src/app/login-success/login-success.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/login-success/login-success.component.html b/frontend/src/app/login-success/login-success.component.html new file mode 100644 index 0000000..bf2b4b5 --- /dev/null +++ b/frontend/src/app/login-success/login-success.component.html @@ -0,0 +1 @@ +

login-success works!

diff --git a/frontend/src/app/login-success/login-success.component.ts b/frontend/src/app/login-success/login-success.component.ts new file mode 100644 index 0000000..5da8136 --- /dev/null +++ b/frontend/src/app/login-success/login-success.component.ts @@ -0,0 +1,25 @@ +import { Component, inject, OnInit } from '@angular/core'; +import { UserService } from '../service/user.service'; +import { KeycloakService } from 'keycloak-angular'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-login-success', + standalone: true, + imports: [], + templateUrl: './login-success.component.html', + styleUrl: './login-success.component.css' +}) +export class LoginSuccessComponent implements OnInit{ + private userService: UserService = inject(UserService); + private keycloakService: KeycloakService = inject(KeycloakService); + private router: Router = inject(Router); + + async ngOnInit() { + const userProfile = await this.keycloakService.loadUserProfile(); + const user = this.userService.getCurrentUser(userProfile); + sessionStorage.setItem('user', JSON.stringify(user)); + + // this.router.navigate(['']); + } +} diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts new file mode 100644 index 0000000..a195238 --- /dev/null +++ b/frontend/src/app/service/user.service.ts @@ -0,0 +1,34 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { KeycloakProfile } from 'keycloak-js'; +import { async } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class UserService { + private http: HttpClient = inject(HttpClient); + + public getUser(id: string) { + return this.http.get<{ keycloakId: string, username: string } | null>(`/backend/user/${id}`); + } + + public createUser(id: string, username: string) { + return this.http.post<{ keycloakId: string, username: string }>('/backend/user', { + keycloakId: id, + username: username, + }); + } + + public async getCurrentUser(userProfile: KeycloakProfile) { + if (userProfile.id == null) { + return; + } + return await this.getUser(userProfile.id).toPromise().then(async user => { + if (user) { + return user; + } + return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise(); + }); + } +} From df9fa9f2753089add8dcf54b911952d09f89e705 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 11:34:49 +0100 Subject: [PATCH 02/11] fix: fix some stuff --- .../de/szut/casino/user/UserController.java | 32 ++++++++++- .../java/de/szut/casino/user/UserEntity.java | 3 + .../szut/casino/user/UserMappingService.java | 22 ++++++++ .../de/szut/casino/user/UserRepository.java | 3 +- .../java/de/szut/casino/user/UserService.java | 56 +++++++++++++++---- .../de/szut/casino/user/dto/GetUserDto.java | 1 + .../szut/casino/user/dto/KeycloakUserDto.java | 15 +++++ .../login-success/login-success.component.ts | 7 ++- frontend/src/app/model/User.ts | 4 ++ frontend/src/app/service/user.service.ts | 13 +++-- 10 files changed, 132 insertions(+), 24 deletions(-) create mode 100644 backend/src/main/java/de/szut/casino/user/UserMappingService.java create mode 100644 backend/src/main/java/de/szut/casino/user/dto/KeycloakUserDto.java create mode 100644 frontend/src/app/model/User.ts diff --git a/backend/src/main/java/de/szut/casino/user/UserController.java b/backend/src/main/java/de/szut/casino/user/UserController.java index 7ee0739..16bdf22 100644 --- a/backend/src/main/java/de/szut/casino/user/UserController.java +++ b/backend/src/main/java/de/szut/casino/user/UserController.java @@ -1,25 +1,55 @@ package de.szut.casino.user; import de.szut.casino.user.dto.CreateUserDto; +import de.szut.casino.user.dto.GetUserDto; import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +@Slf4j @RestController public class UserController { @Autowired private UserService userService; - @GetMapping("/user/{id}") public ResponseEntity getUser(@PathVariable String id) { + if (id == null || !userService.exists(id)) { + return ResponseEntity.notFound().build(); + } return ResponseEntity.ok(userService.getUser(id)); } @PostMapping("/user") public ResponseEntity createUser(@RequestBody @Valid CreateUserDto userData) { + if (userService.exists(userData.getKeycloakId())) { + + return this.redirect("/user/" + userData.getKeycloakId()); + } + return ResponseEntity.ok(userService.createUser(userData)); } + + @GetMapping("/user") + public ResponseEntity getCurrentUser(@RequestHeader("Authorization") String token) { + GetUserDto userData = userService.getCurrentUser(token); + + if (userData == null) { + return ResponseEntity.notFound().build(); + } + + return ResponseEntity.ok(userData); + } + + private ResponseEntity redirect(String route) { + HttpHeaders headers = new HttpHeaders(); + headers.add("Location", route); + + return new ResponseEntity<>(headers, HttpStatus.FOUND); + } } diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java index 3fb45a2..cebd060 100644 --- a/backend/src/main/java/de/szut/casino/user/UserEntity.java +++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java @@ -1,5 +1,6 @@ package de.szut.casino.user; +import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; @@ -13,6 +14,8 @@ public class UserEntity { @Id @GeneratedValue private Long id; + @Column(unique = true) private String keycloakId; private String username; + private float balance; } diff --git a/backend/src/main/java/de/szut/casino/user/UserMappingService.java b/backend/src/main/java/de/szut/casino/user/UserMappingService.java new file mode 100644 index 0000000..3789a1f --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/UserMappingService.java @@ -0,0 +1,22 @@ +package de.szut.casino.user; + +import de.szut.casino.user.dto.CreateUserDto; +import de.szut.casino.user.dto.GetUserDto; +import org.springframework.stereotype.Service; + +@Service +public class UserMappingService { + public GetUserDto mapToGetUserDto(UserEntity user) { + return new GetUserDto(user.getKeycloakId(), user.getUsername(), user.getBalance()); + } + + public UserEntity mapToUserEntity(CreateUserDto createUserDto) { + UserEntity user = new UserEntity(); + user.setKeycloakId(createUserDto.getKeycloakId()); + user.setUsername(createUserDto.getUsername()); + user.setBalance(0); + + return user; + } +} + diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java index f009b44..aaa5752 100644 --- a/backend/src/main/java/de/szut/casino/user/UserRepository.java +++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java @@ -8,9 +8,8 @@ import java.util.Optional; @Service public interface UserRepository extends JpaRepository { - boolean existsByUsername(String username); - @Query("SELECT u FROM UserEntity u WHERE u.keycloakId = ?1") Optional findOneByKeycloakId(String keycloakId); + boolean existsByKeycloakId(String keycloakId); } diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index 95cb01a..c3551ad 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -2,9 +2,21 @@ package de.szut.casino.user; import de.szut.casino.user.dto.CreateUserDto; import de.szut.casino.user.dto.GetUserDto; +import de.szut.casino.user.dto.KeycloakUserDto; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import java.net.URI; +import java.net.http.HttpClient; +import org.springframework.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.time.Duration; +import java.util.List; +import java.util.Map; import java.util.Optional; @Service @@ -12,10 +24,14 @@ public class UserService { @Autowired private UserRepository userRepository; + @Autowired + private RestTemplate http; + + @Autowired + private UserMappingService mappingService; + public UserEntity createUser(CreateUserDto createUserDto) { - UserEntity user = new UserEntity(); - user.setUsername(createUserDto.getUsername()); - user.setKeycloakId(createUserDto.getKeycloakId()); + UserEntity user = mappingService.mapToUserEntity(createUserDto); userRepository.save(user); return user; @@ -23,15 +39,31 @@ public class UserService { public GetUserDto getUser(String keycloakId) { Optional user = this.userRepository.findOneByKeycloakId(keycloakId); - if (user.isPresent()) { - System.out.println(user.get()); - GetUserDto getUserDto = new GetUserDto(); - getUserDto.setKeycloakId(user.get().getKeycloakId()); - getUserDto.setUsername(user.get().getUsername()); - return getUserDto; - } - System.out.println("User not found: "+keycloakId); + return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); - return null; + } + + public GetUserDto getCurrentUser(String token) { + KeycloakUserDto userData = getKeycloakUserInfo(token); + + if (userData == null) { + return null; + } + Optional user = this.userRepository.findOneByKeycloakId(userData.getSub()); + + return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); + + } + + private KeycloakUserDto getKeycloakUserInfo(String token) { + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", token); + ResponseEntity response = this.http.exchange("http://localhost:9090/realms/LF12/protocol/openid-connect/userinfo", HttpMethod.GET, new HttpEntity<>(headers), KeycloakUserDto.class); + + return response.getBody(); + } + + public boolean exists(String keycloakId) { + return userRepository.existsByKeycloakId(keycloakId); } } diff --git a/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java index 95855f1..fb690af 100644 --- a/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java +++ b/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java @@ -12,4 +12,5 @@ import lombok.Setter; public class GetUserDto { private String keycloakId; private String username; + private float balance; } diff --git a/backend/src/main/java/de/szut/casino/user/dto/KeycloakUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/KeycloakUserDto.java new file mode 100644 index 0000000..4238e13 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/dto/KeycloakUserDto.java @@ -0,0 +1,15 @@ +package de.szut.casino.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class KeycloakUserDto { + private String sub; + private String preferred_username; +} diff --git a/frontend/src/app/login-success/login-success.component.ts b/frontend/src/app/login-success/login-success.component.ts index 5da8136..fea0072 100644 --- a/frontend/src/app/login-success/login-success.component.ts +++ b/frontend/src/app/login-success/login-success.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; import { UserService } from '../service/user.service'; import { KeycloakService } from 'keycloak-angular'; import { Router } from '@angular/router'; @@ -8,7 +8,8 @@ import { Router } from '@angular/router'; standalone: true, imports: [], templateUrl: './login-success.component.html', - styleUrl: './login-success.component.css' + styleUrl: './login-success.component.css', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class LoginSuccessComponent implements OnInit{ private userService: UserService = inject(UserService); @@ -17,7 +18,7 @@ export class LoginSuccessComponent implements OnInit{ async ngOnInit() { const userProfile = await this.keycloakService.loadUserProfile(); - const user = this.userService.getCurrentUser(userProfile); + const user = await this.userService.getOrCreateUser(userProfile); sessionStorage.setItem('user', JSON.stringify(user)); // this.router.navigate(['']); diff --git a/frontend/src/app/model/User.ts b/frontend/src/app/model/User.ts new file mode 100644 index 0000000..28081e4 --- /dev/null +++ b/frontend/src/app/model/User.ts @@ -0,0 +1,4 @@ +export interface User { + keycloakId: string; + username: string; +} diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index a195238..00bafe1 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -1,7 +1,8 @@ import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { KeycloakProfile } from 'keycloak-js'; -import { async } from 'rxjs'; +import { async, Observable } from 'rxjs'; +import { User } from '../model/User'; @Injectable({ providedIn: 'root', @@ -9,18 +10,18 @@ import { async } from 'rxjs'; export class UserService { private http: HttpClient = inject(HttpClient); - public getUser(id: string) { - return this.http.get<{ keycloakId: string, username: string } | null>(`/backend/user/${id}`); + public getUser(id: string): Observable { + return this.http.get(`/backend/user/${id}`); } - public createUser(id: string, username: string) { - return this.http.post<{ keycloakId: string, username: string }>('/backend/user', { + public createUser(id: string, username: string): Observable { + return this.http.post('/backend/user', { keycloakId: id, username: username, }); } - public async getCurrentUser(userProfile: KeycloakProfile) { + public async getOrCreateUser(userProfile: KeycloakProfile) { if (userProfile.id == null) { return; } From 2326d41a9641630303ee732fa56010e6a988f47d Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 11:39:03 +0100 Subject: [PATCH 03/11] satisfy quality tools --- frontend/src/app/auth.guard.ts | 7 ++----- .../login-success/login-success.component.ts | 4 ++-- frontend/src/app/service/user.service.ts | 20 ++++++++++--------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/auth.guard.ts b/frontend/src/app/auth.guard.ts index e97e4fc..035ccc8 100644 --- a/frontend/src/app/auth.guard.ts +++ b/frontend/src/app/auth.guard.ts @@ -1,11 +1,8 @@ -import { ActivatedRouteSnapshot, CanActivateFn, Router, RouterStateSnapshot } from '@angular/router'; +import { CanActivateFn, Router } from '@angular/router'; import { inject } from '@angular/core'; import { KeycloakService } from 'keycloak-angular'; -export const authGuard: CanActivateFn = async ( - route: ActivatedRouteSnapshot, - state: RouterStateSnapshot -) => { +export const authGuard: CanActivateFn = async () => { const keycloakService = inject(KeycloakService); const router = inject(Router); diff --git a/frontend/src/app/login-success/login-success.component.ts b/frontend/src/app/login-success/login-success.component.ts index fea0072..f0608b4 100644 --- a/frontend/src/app/login-success/login-success.component.ts +++ b/frontend/src/app/login-success/login-success.component.ts @@ -11,7 +11,7 @@ import { Router } from '@angular/router'; styleUrl: './login-success.component.css', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class LoginSuccessComponent implements OnInit{ +export class LoginSuccessComponent implements OnInit { private userService: UserService = inject(UserService); private keycloakService: KeycloakService = inject(KeycloakService); private router: Router = inject(Router); @@ -21,6 +21,6 @@ export class LoginSuccessComponent implements OnInit{ const user = await this.userService.getOrCreateUser(userProfile); sessionStorage.setItem('user', JSON.stringify(user)); - // this.router.navigate(['']); + this.router.navigate(['']); } } diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index 00bafe1..1f415a3 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { KeycloakProfile } from 'keycloak-js'; -import { async, Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { User } from '../model/User'; @Injectable({ @@ -10,8 +10,8 @@ import { User } from '../model/User'; export class UserService { private http: HttpClient = inject(HttpClient); - public getUser(id: string): Observable { - return this.http.get(`/backend/user/${id}`); + public getUser(id: string): Observable { + return this.http.get(`/backend/user/${id}`); } public createUser(id: string, username: string): Observable { @@ -25,11 +25,13 @@ export class UserService { if (userProfile.id == null) { return; } - return await this.getUser(userProfile.id).toPromise().then(async user => { - if (user) { - return user; - } - return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise(); - }); + return await this.getUser(userProfile.id) + .toPromise() + .then(async (user) => { + if (user) { + return user; + } + return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise(); + }); } } From 5afdbad461512ba5358de60e576c3e1cb606895c Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 11:41:17 +0100 Subject: [PATCH 04/11] refactor --- .../src/main/java/de/szut/casino/user/UserEntity.java | 9 +++++++++ .../java/de/szut/casino/user/UserMappingService.java | 7 +------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java index cebd060..f6c1a5b 100644 --- a/backend/src/main/java/de/szut/casino/user/UserEntity.java +++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java @@ -4,12 +4,15 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; @Setter @Getter @Entity +@NoArgsConstructor public class UserEntity { @Id @GeneratedValue @@ -18,4 +21,10 @@ public class UserEntity { private String keycloakId; private String username; private float balance; + + public UserEntity(String keycloakId, String username, float balance) { + this.keycloakId = keycloakId; + this.username = username; + this.balance = balance; + } } diff --git a/backend/src/main/java/de/szut/casino/user/UserMappingService.java b/backend/src/main/java/de/szut/casino/user/UserMappingService.java index 3789a1f..578ba49 100644 --- a/backend/src/main/java/de/szut/casino/user/UserMappingService.java +++ b/backend/src/main/java/de/szut/casino/user/UserMappingService.java @@ -11,12 +11,7 @@ public class UserMappingService { } public UserEntity mapToUserEntity(CreateUserDto createUserDto) { - UserEntity user = new UserEntity(); - user.setKeycloakId(createUserDto.getKeycloakId()); - user.setUsername(createUserDto.getUsername()); - user.setBalance(0); - - return user; + return new UserEntity(createUserDto.getUsername(), createUserDto.getKeycloakId(), 0); } } From a091387c1c9dda323c9928a95d5cfadba944d7a7 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 11:59:36 +0100 Subject: [PATCH 05/11] fix: 500 when loggin in --- .../src/main/java/de/szut/casino/user/UserMappingService.java | 2 +- frontend/src/app/service/user.service.ts | 4 ++-- frontend/src/app/shared/components/navbar/navbar.component.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/user/UserMappingService.java b/backend/src/main/java/de/szut/casino/user/UserMappingService.java index 578ba49..e0183ec 100644 --- a/backend/src/main/java/de/szut/casino/user/UserMappingService.java +++ b/backend/src/main/java/de/szut/casino/user/UserMappingService.java @@ -11,7 +11,7 @@ public class UserMappingService { } public UserEntity mapToUserEntity(CreateUserDto createUserDto) { - return new UserEntity(createUserDto.getUsername(), createUserDto.getKeycloakId(), 0); + return new UserEntity(createUserDto.getKeycloakId(), createUserDto.getUsername(), 0); } } diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index 1f415a3..927ba1c 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -1,7 +1,7 @@ import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { KeycloakProfile } from 'keycloak-js'; -import { Observable } from 'rxjs'; +import { catchError, EMPTY, Observable } from 'rxjs'; import { User } from '../model/User'; @Injectable({ @@ -11,7 +11,7 @@ export class UserService { private http: HttpClient = inject(HttpClient); public getUser(id: string): Observable { - return this.http.get(`/backend/user/${id}`); + return this.http.get(`/backend/user/${id}`).pipe(catchError(() => EMPTY)); } public createUser(id: string, username: string): Observable { diff --git a/frontend/src/app/shared/components/navbar/navbar.component.ts b/frontend/src/app/shared/components/navbar/navbar.component.ts index ba86e73..53d1dee 100644 --- a/frontend/src/app/shared/components/navbar/navbar.component.ts +++ b/frontend/src/app/shared/components/navbar/navbar.component.ts @@ -18,7 +18,7 @@ export class NavbarComponent { login() { try { const baseUrl = window.location.origin; - this.keycloakService.login({ redirectUri: `${baseUrl}/home` }); + this.keycloakService.login({ redirectUri: `${baseUrl}/login/success` }); } catch (error) { console.error('Login failed:', error); } From 877b6f77b2c55870dcef257967cabae5a9fbeb52 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 12:06:49 +0100 Subject: [PATCH 06/11] chore: move classes add balance fix routes --- frontend/src/app/app.config.ts | 2 +- frontend/src/app/app.routes.ts | 2 +- .../app/{ => feature}/login-success/login-success.component.css | 0 .../{ => feature}/login-success/login-success.component.html | 0 .../app/{ => feature}/login-success/login-success.component.ts | 2 +- frontend/src/app/model/User.ts | 1 + 6 files changed, 4 insertions(+), 3 deletions(-) rename frontend/src/app/{ => feature}/login-success/login-success.component.css (100%) rename frontend/src/app/{ => feature}/login-success/login-success.component.html (100%) rename frontend/src/app/{ => feature}/login-success/login-success.component.ts (93%) diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index cbe2fcf..217efd4 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -27,7 +27,7 @@ export const initializeKeycloak = (keycloak: KeycloakService) => async () => onLoad: 'check-sso', silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', checkLoginIframe: false, - redirectUri: window.location.origin + '/login/success', + redirectUri: window.location.origin + '/', }, }); diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index bbf1a56..c4226c9 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -2,7 +2,7 @@ import { Routes } from '@angular/router'; import { LandingComponent } from './feature/landing/landing.component'; import { HomeComponent } from './feature/home/home.component'; import { authGuard } from './auth.guard'; -import { LoginSuccessComponent } from './login-success/login-success.component'; +import { LoginSuccessComponent } from './feature/login-success/login-success.component'; export const routes: Routes = [ { diff --git a/frontend/src/app/login-success/login-success.component.css b/frontend/src/app/feature/login-success/login-success.component.css similarity index 100% rename from frontend/src/app/login-success/login-success.component.css rename to frontend/src/app/feature/login-success/login-success.component.css diff --git a/frontend/src/app/login-success/login-success.component.html b/frontend/src/app/feature/login-success/login-success.component.html similarity index 100% rename from frontend/src/app/login-success/login-success.component.html rename to frontend/src/app/feature/login-success/login-success.component.html diff --git a/frontend/src/app/login-success/login-success.component.ts b/frontend/src/app/feature/login-success/login-success.component.ts similarity index 93% rename from frontend/src/app/login-success/login-success.component.ts rename to frontend/src/app/feature/login-success/login-success.component.ts index f0608b4..e0882f6 100644 --- a/frontend/src/app/login-success/login-success.component.ts +++ b/frontend/src/app/feature/login-success/login-success.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; -import { UserService } from '../service/user.service'; +import { UserService } from '../../service/user.service'; import { KeycloakService } from 'keycloak-angular'; import { Router } from '@angular/router'; diff --git a/frontend/src/app/model/User.ts b/frontend/src/app/model/User.ts index 28081e4..a579b7a 100644 --- a/frontend/src/app/model/User.ts +++ b/frontend/src/app/model/User.ts @@ -1,4 +1,5 @@ export interface User { keycloakId: string; username: string; + balance: number; } From 8d4901601f2785cd4cdeda409fd8092c4cf26e7d Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 12:09:53 +0100 Subject: [PATCH 07/11] chore: adjust login success view --- .../src/app/feature/login-success/login-success.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/feature/login-success/login-success.component.html b/frontend/src/app/feature/login-success/login-success.component.html index bf2b4b5..ba9d449 100644 --- a/frontend/src/app/feature/login-success/login-success.component.html +++ b/frontend/src/app/feature/login-success/login-success.component.html @@ -1 +1 @@ -

login-success works!

+

Logging in...

From 6c025cc8d2ca94664448c7b03162b9c46a6b9b69 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 12:18:12 +0100 Subject: [PATCH 08/11] fix: lazy load login success component --- backend/src/main/java/de/szut/casino/user/UserService.java | 2 +- frontend/src/app/app.routes.ts | 3 +-- .../src/app/feature/login-success/login-success.component.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index c3551ad..d114052 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -59,7 +59,7 @@ public class UserService { HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", token); ResponseEntity response = this.http.exchange("http://localhost:9090/realms/LF12/protocol/openid-connect/userinfo", HttpMethod.GET, new HttpEntity<>(headers), KeycloakUserDto.class); - + return response.getBody(); } diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index c4226c9..afab709 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -2,7 +2,6 @@ import { Routes } from '@angular/router'; import { LandingComponent } from './feature/landing/landing.component'; import { HomeComponent } from './feature/home/home.component'; import { authGuard } from './auth.guard'; -import { LoginSuccessComponent } from './feature/login-success/login-success.component'; export const routes: Routes = [ { @@ -11,7 +10,7 @@ export const routes: Routes = [ }, { path: 'login/success', - component: LoginSuccessComponent, + loadComponent: () => import('./feature/login-success/login-success.component'), }, { path: 'home', diff --git a/frontend/src/app/feature/login-success/login-success.component.ts b/frontend/src/app/feature/login-success/login-success.component.ts index e0882f6..bf81ac6 100644 --- a/frontend/src/app/feature/login-success/login-success.component.ts +++ b/frontend/src/app/feature/login-success/login-success.component.ts @@ -11,7 +11,7 @@ import { Router } from '@angular/router'; styleUrl: './login-success.component.css', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class LoginSuccessComponent implements OnInit { +export default class LoginSuccessComponent implements OnInit { private userService: UserService = inject(UserService); private keycloakService: KeycloakService = inject(KeycloakService); private router: Router = inject(Router); From 7bd06dee62a62a600207cbf5d62b5edc259c3e5d Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 19 Feb 2025 12:23:45 +0100 Subject: [PATCH 09/11] refactor(user): reorganize imports and code structure --- .../de/szut/casino/user/UserController.java | 17 ++++++++++++----- .../java/de/szut/casino/user/UserService.java | 19 +++++++------------ frontend/src/app/app.routes.ts | 5 ++--- frontend/src/app/service/user.service.ts | 1 + 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/user/UserController.java b/backend/src/main/java/de/szut/casino/user/UserController.java index 16bdf22..4d232ac 100644 --- a/backend/src/main/java/de/szut/casino/user/UserController.java +++ b/backend/src/main/java/de/szut/casino/user/UserController.java @@ -1,14 +1,20 @@ package de.szut.casino.user; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + import de.szut.casino.user.dto.CreateUserDto; import de.szut.casino.user.dto.GetUserDto; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; @Slf4j @RestController @@ -22,6 +28,7 @@ public class UserController { if (id == null || !userService.exists(id)) { return ResponseEntity.notFound().build(); } + return ResponseEntity.ok(userService.getUser(id)); } diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index d114052..724962e 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -1,23 +1,18 @@ package de.szut.casino.user; -import de.szut.casino.user.dto.CreateUserDto; -import de.szut.casino.user.dto.GetUserDto; -import de.szut.casino.user.dto.KeycloakUserDto; +import java.util.Optional; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import java.net.URI; -import java.net.http.HttpClient; -import org.springframework.http.HttpHeaders; -import java.net.http.HttpRequest; -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import de.szut.casino.user.dto.CreateUserDto; +import de.szut.casino.user.dto.GetUserDto; +import de.szut.casino.user.dto.KeycloakUserDto; @Service public class UserService { @@ -39,8 +34,8 @@ public class UserService { public GetUserDto getUser(String keycloakId) { Optional user = this.userRepository.findOneByKeycloakId(keycloakId); - return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); + return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(null); } public GetUserDto getCurrentUser(String token) { diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index afab709..c7cfcf3 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,6 +1,5 @@ import { Routes } from '@angular/router'; import { LandingComponent } from './feature/landing/landing.component'; -import { HomeComponent } from './feature/home/home.component'; import { authGuard } from './auth.guard'; export const routes: Routes = [ @@ -11,10 +10,10 @@ export const routes: Routes = [ { path: 'login/success', loadComponent: () => import('./feature/login-success/login-success.component'), - }, + }, { path: 'home', - component: HomeComponent, + loadComponent: () => import('./feature/home/home.component'), canActivate: [authGuard], }, ]; diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index 927ba1c..2f9c22f 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -31,6 +31,7 @@ export class UserService { if (user) { return user; } + return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise(); }); } From da047eef70296e74384a40cd85c0c9358e6a74e0 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 12:31:07 +0100 Subject: [PATCH 10/11] style: prettier --- frontend/src/app/app.routes.ts | 2 +- frontend/src/app/service/user.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index c7cfcf3..6fbef95 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -10,7 +10,7 @@ export const routes: Routes = [ { path: 'login/success', loadComponent: () => import('./feature/login-success/login-success.component'), - }, + }, { path: 'home', loadComponent: () => import('./feature/home/home.component'), diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts index 2f9c22f..ba6bead 100644 --- a/frontend/src/app/service/user.service.ts +++ b/frontend/src/app/service/user.service.ts @@ -31,7 +31,7 @@ export class UserService { if (user) { return user; } - + return await this.createUser(userProfile.id ?? '', userProfile.username ?? '').toPromise(); }); } From 11c6634d6cc2e9dc3f46d77a28ba7a8a6b56c32d Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 19 Feb 2025 12:50:13 +0100 Subject: [PATCH 11/11] fix: build --- frontend/src/app/feature/home/home.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/feature/home/home.component.ts b/frontend/src/app/feature/home/home.component.ts index 7475214..d560eb8 100644 --- a/frontend/src/app/feature/home/home.component.ts +++ b/frontend/src/app/feature/home/home.component.ts @@ -11,7 +11,7 @@ import { NavbarComponent } from '../../shared/components/navbar/navbar.component templateUrl: './home.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class HomeComponent { +export default class HomeComponent { private keycloakService: KeycloakService = inject(KeycloakService); public dialog: MatDialog = inject(MatDialog);