diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 97dbeeb..366770a 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -17,15 +17,16 @@ jobs: with: distribution: "temurin" java-version: "22" - - name: "Cache Gradle dependencies" - uses: actions/cache@v3 + + - uses: actions/cache@v3 + working-directory: ./backend with: path: | ~/.gradle/caches ~/.gradle/wrapper - key: gradle-${{ runner.os }}-common + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | - gradle-${{ runner.os }}- + ${{ runner.os }}-gradle- - name: "Prepare Gradle" working-directory: ./backend run: gradle clean @@ -46,14 +47,6 @@ jobs: uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v2 - - uses: actions/cache@v3 - working-directory: ./frontend - with: - path: | - frontend/node_modules/ - key: ${{ runner.os }}-bun- - restore-keys: | - ${{ runner.os }}-bun- - name: Install dependencies run: | cd frontend @@ -73,14 +66,6 @@ jobs: uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v2 - - uses: actions/cache@v3 - working-directory: ./frontend - with: - path: | - frontend/node_modules/ - key: ${{ runner.os }}-bun- - restore-keys: | - ${{ runner.os }}-bun- - name: Install dependencies run: | cd frontend @@ -100,22 +85,6 @@ jobs: uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v2 - - uses: actions/cache@v3 - working-directory: ./frontend - with: - path: | - frontend/node_modules/ - key: ${{ runner.os }}-bun- - restore-keys: | - ${{ runner.os }}-bun- - - uses: actions/cache@v3 - working-directory: ./frontend - with: - path: | - frontend/dist/ - key: ${{ runner.os }}-dist- - restore-keys: | - ${{ runner.os }}-dist- - name: Install dependencies run: | cd frontend diff --git a/backend/src/main/java/de/szut/casino/security/KeycloakLogoutHandler.java b/backend/src/main/java/de/szut/casino/security/KeycloakLogoutHandler.java deleted file mode 100644 index 5e08794..0000000 --- a/backend/src/main/java/de/szut/casino/security/KeycloakLogoutHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.szut.casino.security; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -@Slf4j -@Component -public class KeycloakLogoutHandler implements LogoutHandler { - - - private final RestTemplate restTemplate; - - public KeycloakLogoutHandler(RestTemplate restTemplate) { - this.restTemplate = restTemplate; - } - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication auth) { - logout(request, auth); - } - - public void logout(HttpServletRequest request, Authentication auth) { - logoutFromKeycloak((OidcUser) auth.getPrincipal()); - } - - private void logoutFromKeycloak(OidcUser user) { - String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout"; - UriComponentsBuilder builder = UriComponentsBuilder - .fromUriString(endSessionEndpoint) - .queryParam("id_token_hint", user.getIdToken().getTokenValue()); - - ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class); - if (logoutResponse.getStatusCode().is2xxSuccessful()) { - log.info("Successfulley logged out from Keycloak"); - } else { - log.error("Could not propagate logout to Keycloak"); - } - } - -} diff --git a/backend/src/main/java/de/szut/casino/security/KeycloakSecurityConfig.java b/backend/src/main/java/de/szut/casino/security/KeycloakSecurityConfig.java index 8ddcbcb..0654bb6 100644 --- a/backend/src/main/java/de/szut/casino/security/KeycloakSecurityConfig.java +++ b/backend/src/main/java/de/szut/casino/security/KeycloakSecurityConfig.java @@ -14,7 +14,6 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.session.HttpSessionEventPublisher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import java.util.ArrayList; import java.util.List; @@ -24,16 +23,9 @@ import java.util.Map; @EnableWebSecurity class KeycloakSecurityConfig { - private static final String GROUPS = "groups"; private static final String REALM_ACCESS_CLAIM = "realm_access"; private static final String ROLES_CLAIM = "roles"; - private final KeycloakLogoutHandler keycloakLogoutHandler; - - KeycloakSecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) { - this.keycloakLogoutHandler = keycloakLogoutHandler; - } - @Bean public SessionRegistry sessionRegistry() { return new SessionRegistryImpl(); @@ -49,11 +41,10 @@ class KeycloakSecurityConfig { return new HttpSessionEventPublisher(); } - @Bean public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth -> auth - .requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**", "/health").permitAll() + .requestMatchers("/swagger", "/swagger-ui/**", "/v3/api-docs/**", "health").permitAll() .anyRequest().authenticated() ) .oauth2ResourceServer(spec -> spec.jwt(Customizer.withDefaults())); @@ -67,9 +58,9 @@ class KeycloakSecurityConfig { jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwt -> { List grantedAuthorities = new ArrayList<>(); - Map realmAccess = jwt.getClaim("realm_access"); - if (realmAccess != null && realmAccess.containsKey("roles")) { - List roles = (List) realmAccess.get("roles"); + Map realmAccess = jwt.getClaim(REALM_ACCESS_CLAIM); + if (realmAccess != null && realmAccess.containsKey(ROLES_CLAIM)) { + List roles = (List) realmAccess.get(ROLES_CLAIM); for (String role : roles) { grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role)); } diff --git a/backend/src/main/java/de/szut/casino/user/UserController.java b/backend/src/main/java/de/szut/casino/user/UserController.java deleted file mode 100644 index 4d232ac..0000000 --- a/backend/src/main/java/de/szut/casino/user/UserController.java +++ /dev/null @@ -1,62 +0,0 @@ -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; - -@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 deleted file mode 100644 index f6c1a5b..0000000 --- a/backend/src/main/java/de/szut/casino/user/UserEntity.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.szut.casino.user; - -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 - private Long id; - @Column(unique = true) - 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 deleted file mode 100644 index e0183ec..0000000 --- a/backend/src/main/java/de/szut/casino/user/UserMappingService.java +++ /dev/null @@ -1,17 +0,0 @@ -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) { - return new UserEntity(createUserDto.getKeycloakId(), createUserDto.getUsername(), 0); - } -} - diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java deleted file mode 100644 index aaa5752..0000000 --- a/backend/src/main/java/de/szut/casino/user/UserRepository.java +++ /dev/null @@ -1,15 +0,0 @@ -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 { - @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 deleted file mode 100644 index 724962e..0000000 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ /dev/null @@ -1,64 +0,0 @@ -package de.szut.casino.user; - -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 de.szut.casino.user.dto.CreateUserDto; -import de.szut.casino.user.dto.GetUserDto; -import de.szut.casino.user.dto.KeycloakUserDto; - -@Service -public class UserService { - @Autowired - private UserRepository userRepository; - - @Autowired - private RestTemplate http; - - @Autowired - private UserMappingService mappingService; - - public UserEntity createUser(CreateUserDto createUserDto) { - UserEntity user = mappingService.mapToUserEntity(createUserDto); - userRepository.save(user); - - return user; - } - - public GetUserDto getUser(String keycloakId) { - Optional user = this.userRepository.findOneByKeycloakId(keycloakId); - - return user.map(userEntity -> mappingService.mapToGetUserDto(userEntity)).orElse(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/CreateUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java deleted file mode 100644 index ff28427..0000000 --- a/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index fb690af..0000000 --- a/backend/src/main/java/de/szut/casino/user/dto/GetUserDto.java +++ /dev/null @@ -1,16 +0,0 @@ -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; - 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 deleted file mode 100644 index 4238e13..0000000 --- a/backend/src/main/java/de/szut/casino/user/dto/KeycloakUserDto.java +++ /dev/null @@ -1,15 +0,0 @@ -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/docker/docker-compose.yml b/docker/docker-compose.yml index 2442d38..0b7e617 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,3 +1,5 @@ +version: '3' + volumes: keycloak_data: postgres_data_keycloak_db: diff --git a/frontend/angular.json b/frontend/angular.json index f7a1430..a6a1c7c 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -24,6 +24,7 @@ } ], "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [] @@ -78,6 +79,7 @@ } ], "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [] diff --git a/frontend/bun.lock b/frontend/bun.lock index 91de068..f09b071 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -14,10 +14,6 @@ "@angular/platform-browser": "^18.2.0", "@angular/platform-browser-dynamic": "^18.2.0", "@angular/router": "^18.2.0", - "@fortawesome/angular-fontawesome": "^1.0.0", - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-brands-svg-icons": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", "@stripe/stripe-js": "^5.6.0", "@tailwindcss/postcss": "^4.0.3", "keycloak-angular": "^16.0.1", @@ -377,16 +373,6 @@ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.5", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="], - "@fortawesome/angular-fontawesome": ["@fortawesome/angular-fontawesome@1.0.0", "", { "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.1", "tslib": "^2.8.1" }, "peerDependencies": { "@angular/core": "^19.0.0" } }, "sha512-EC2fYuXIuw2ld1kzJi+zysWus6OeGGfLQtbh0hW9zyyq5aBo8ZJkcJKBsVQ8E6Mg7nHyTWaXn+sdcXTPDWz+UQ=="], - - "@fortawesome/fontawesome-common-types": ["@fortawesome/fontawesome-common-types@6.7.2", "", {}, "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg=="], - - "@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.7.2", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" } }, "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA=="], - - "@fortawesome/free-brands-svg-icons": ["@fortawesome/free-brands-svg-icons@6.7.2", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" } }, "sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q=="], - - "@fortawesome/free-solid-svg-icons": ["@fortawesome/free-solid-svg-icons@6.7.2", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.7.2" } }, "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA=="], - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], diff --git a/frontend/package.json b/frontend/package.json index c79aae0..c6be094 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -23,10 +23,6 @@ "@angular/platform-browser": "^18.2.0", "@angular/platform-browser-dynamic": "^18.2.0", "@angular/router": "^18.2.0", - "@fortawesome/angular-fontawesome": "^1.0.0", - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-brands-svg-icons": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", "@stripe/stripe-js": "^5.6.0", "@tailwindcss/postcss": "^4.0.3", "keycloak-angular": "^16.0.1", @@ -53,4 +49,4 @@ "typescript": "~5.5.2", "typescript-eslint": "8.23.0" } -} +} \ No newline at end of file diff --git a/frontend/public/blackjack.webp b/frontend/public/blackjack.webp deleted file mode 100644 index a791c14..0000000 Binary files a/frontend/public/blackjack.webp and /dev/null differ diff --git a/frontend/public/liars-dice.webp b/frontend/public/liars-dice.webp deleted file mode 100644 index df1fd1c..0000000 Binary files a/frontend/public/liars-dice.webp and /dev/null differ diff --git a/frontend/public/lootbox.webp b/frontend/public/lootbox.webp deleted file mode 100644 index 710deed..0000000 Binary files a/frontend/public/lootbox.webp and /dev/null differ diff --git a/frontend/public/plinko.webp b/frontend/public/plinko.webp deleted file mode 100644 index c11370b..0000000 Binary files a/frontend/public/plinko.webp and /dev/null differ diff --git a/frontend/public/poker.webp b/frontend/public/poker.webp deleted file mode 100644 index 9c60024..0000000 Binary files a/frontend/public/poker.webp and /dev/null differ diff --git a/frontend/public/slots.webp b/frontend/public/slots.webp deleted file mode 100644 index 5cf639a..0000000 Binary files a/frontend/public/slots.webp and /dev/null differ diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 41260d2..0680b43 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,6 +1 @@ -
-
- -
- -
+ diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index bbc5fb6..7dea888 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -2,12 +2,11 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterOutlet } from '@angular/router'; import { KeycloakAngularModule } from 'keycloak-angular'; -import { FooterComponent } from './shared/components/footer/footer.component'; @Component({ selector: 'app-root', standalone: true, - imports: [CommonModule, RouterOutlet, KeycloakAngularModule, FooterComponent], + imports: [CommonModule, RouterOutlet, KeycloakAngularModule], providers: [], templateUrl: './app.component.html', styleUrl: './app.component.css', diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index 217efd4..c6b9f77 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -4,7 +4,6 @@ import { provideExperimentalZonelessChangeDetection, } from '@angular/core'; import { provideRouter } from '@angular/router'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { routes } from './app.routes'; import { @@ -27,7 +26,7 @@ export const initializeKeycloak = (keycloak: KeycloakService) => async () => onLoad: 'check-sso', silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', checkLoginIframe: false, - redirectUri: window.location.origin + '/', + redirectUri: 'http://localhost:4200', }, }); @@ -39,7 +38,6 @@ export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes), KeycloakAngularModule, - FontAwesomeModule, { provide: APP_INITIALIZER, useFactory: initializeApp, diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 6fbef95..73ed20c 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,5 +1,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'; export const routes: Routes = [ @@ -7,13 +8,9 @@ export const routes: Routes = [ path: '', component: LandingComponent, }, - { - path: 'login/success', - loadComponent: () => import('./feature/login-success/login-success.component'), - }, { path: 'home', - loadComponent: () => import('./feature/home/home.component'), + component: HomeComponent, canActivate: [authGuard], }, ]; diff --git a/frontend/src/app/auth.guard.ts b/frontend/src/app/auth.guard.ts index 035ccc8..0743ff6 100644 --- a/frontend/src/app/auth.guard.ts +++ b/frontend/src/app/auth.guard.ts @@ -1,16 +1,23 @@ -import { CanActivateFn, Router } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; import { inject } from '@angular/core'; import { KeycloakService } from 'keycloak-angular'; -export const authGuard: CanActivateFn = async () => { +export const authGuard: CanActivateFn = async ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot +) => { const keycloakService = inject(KeycloakService); - const router = inject(Router); + const isLoggedIn = keycloakService.isLoggedIn(); - if (keycloakService.isLoggedIn()) { + if (isLoggedIn) { return true; } - router.navigate(['']); + const baseurl = window.location.origin; + + keycloakService.login({ + redirectUri: `${baseurl}${state.url}`, + }); return false; }; diff --git a/frontend/src/app/feature/deposit/deposit.component.ts b/frontend/src/app/feature/deposit/deposit.component.ts index 36ec63f..6b8393d 100644 --- a/frontend/src/app/feature/deposit/deposit.component.ts +++ b/frontend/src/app/feature/deposit/deposit.component.ts @@ -7,6 +7,7 @@ import { environment } from '../../../environments/environment'; import { NgIf } from '@angular/common'; import { MatDialogActions, + MatDialogClose, MatDialogContent, MatDialogRef, MatDialogTitle, @@ -22,6 +23,7 @@ import { MatButton } from '@angular/material/button'; MatDialogTitle, MatDialogContent, MatDialogActions, + MatDialogClose, MatButton, ], templateUrl: './deposit.component.html', diff --git a/frontend/src/app/feature/home/home.component.html b/frontend/src/app/feature/home/home.component.html index 56a49cf..b85ce29 100644 --- a/frontend/src/app/feature/home/home.component.html +++ b/frontend/src/app/feature/home/home.component.html @@ -1,91 +1,29 @@ - -
-
-
+ -
-
-
-

Beliebte Spiele

-
- - -
-
- -
-
-
-
- -
-
-

{{ game.name }}

- -
-
-
-
-
-
- -
-

Alle Spiele

-
-
-
- -
-
-

{{ game.name }}

- -
-
-
-
-
-
-
- -
-
-

Konto

-
- - -
-
- -
-

Letzte Transaktionen

-
-
-
-

{{ transaction.type }}

-

{{ transaction.date }}

-
- - {{ transaction.amount | currency: 'EUR' }} - -
-
-
-
+
+
+

Spiel Vorschau

+

Spiel Name

+ +
+
+

Spiel Vorschau

+

Spiel Name

+ +
+
+

Spiel Vorschau

+

Spiel Name

+
diff --git a/frontend/src/app/feature/home/home.component.ts b/frontend/src/app/feature/home/home.component.ts index ebb2f02..a4b2b8b 100644 --- a/frontend/src/app/feature/home/home.component.ts +++ b/frontend/src/app/feature/home/home.component.ts @@ -2,93 +2,18 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { KeycloakService } from 'keycloak-angular'; import { MatDialog } from '@angular/material/dialog'; import { DepositComponent } from '../deposit/deposit.component'; -import { NavbarComponent } from '../../shared/components/navbar/navbar.component'; -import { CurrencyPipe, NgFor } from '@angular/common'; - -interface Game { - id: string; - name: string; - image: string; -} -interface Transaction { - id: string; - type: string; - amount: number; - date: string; -} @Component({ selector: 'app-homepage', standalone: true, - imports: [NavbarComponent, CurrencyPipe, NgFor], + imports: [], templateUrl: './home.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export default class HomeComponent { +export class HomeComponent { private keycloakService: KeycloakService = inject(KeycloakService); public dialog: MatDialog = inject(MatDialog); - userAvatar = '/assets/images/default-avatar.png'; - username = this.keycloakService.getUsername(); - vipLevel = 1; - balance = 1000.0; - - featuredGames: Game[] = [ - { - id: '1', - name: 'Poker', - image: '/poker.webp', - }, - { - id: '2', - name: 'Blackjack', - image: '/blackjack.webp', - }, - { - id: '3', - name: 'Slots', - image: '/slots.webp', - }, - { - id: '4', - name: 'Plinko', - image: '/plinko.webp', - }, - { - id: '5', - name: 'Liars Dice', - image: '/liars-dice.webp', - }, - { - id: '6', - name: 'Lootboxen', - image: '/lootbox.webp', - }, - ]; - - allGames: Game[] = [...this.featuredGames]; - - recentTransactions: Transaction[] = [ - { - id: '1', - type: 'Deposit', - amount: 100.0, - date: '2024-03-20', - }, - { - id: '2', - type: 'Withdrawal', - amount: -50.0, - date: '2024-03-19', - }, - { - id: '3', - type: 'Bonus', - amount: 25.0, - date: '2024-03-18', - }, - ]; - public logout() { const baseUrl = window.location.origin; diff --git a/frontend/src/app/feature/landing/landing.component.html b/frontend/src/app/feature/landing/landing.component.html index 597c71a..80d5974 100644 --- a/frontend/src/app/feature/landing/landing.component.html +++ b/frontend/src/app/feature/landing/landing.component.html @@ -1,142 +1,5 @@ - - -
-
-
-
-

- Willkommensbonus -

-
200% bis zu 500€
-

+ 200 Freispiele

- - -
- -
-

Beliebte Spiele

-
-
-
-
-
-
-

Slots

-

Klassische Spielautomaten

- -
-
-
-
-

Plinko

-

Spannendes Geschicklichkeitsspiel

- -
-
- -
- -
-
-
-

Poker

-

Texas Hold'em & mehr

- -
-
-
-
-

Liars Dice

-

Würfelspiel mit Strategie

- -
-
- -
-
-
- - - - -
- -
-
-
- -
-
-
50 Mio.€+
-
Ausgezahlt
-
- -
-
10 Mio.+
-
Spiele
-
- -
-
24/7
-
Support *
-
-
-
-
-
+@if (isLoggedIn) { + +} @else { + +} diff --git a/frontend/src/app/feature/landing/landing.component.ts b/frontend/src/app/feature/landing/landing.component.ts index 7fa92b6..d2e28c0 100644 --- a/frontend/src/app/feature/landing/landing.component.ts +++ b/frontend/src/app/feature/landing/landing.component.ts @@ -1,55 +1,21 @@ -import { ChangeDetectionStrategy, Component, OnInit, OnDestroy } from '@angular/core'; -import { NavbarComponent } from '../../shared/components/navbar/navbar.component'; -import { NgFor } from '@angular/common'; +import { Component, inject } from '@angular/core'; +import { KeycloakService } from 'keycloak-angular'; +import { RouterLink } from '@angular/router'; @Component({ - selector: 'app-landing-page', + selector: 'app-landing', standalone: true, - imports: [NavbarComponent, NgFor], + imports: [RouterLink], templateUrl: './landing.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, }) -export class LandingComponent implements OnInit, OnDestroy { - currentSlide = 0; - private autoplayInterval: ReturnType | undefined; +export class LandingComponent { + private keycloakService: KeycloakService = inject(KeycloakService); - ngOnInit() { - this.startAutoplay(); - } + public isLoggedIn = this.keycloakService.isLoggedIn(); - ngOnDestroy() { - this.stopAutoplay(); - } + public login() { + const baseUrl = window.location.origin; - prevSlide() { - this.currentSlide = this.currentSlide === 0 ? 1 : 0; - this.resetAutoplay(); - } - - nextSlide() { - this.currentSlide = this.currentSlide === 1 ? 0 : 1; - this.resetAutoplay(); - } - - goToSlide(index: number) { - this.currentSlide = index; - this.resetAutoplay(); - } - - private startAutoplay() { - this.autoplayInterval = setInterval(() => { - this.nextSlide(); - }, 5000); - } - - private stopAutoplay() { - if (this.autoplayInterval) { - clearInterval(this.autoplayInterval); - } - } - - private resetAutoplay() { - this.stopAutoplay(); - this.startAutoplay(); + this.keycloakService.login({ redirectUri: `${baseUrl}/home` }); } } diff --git a/frontend/src/app/feature/login-success/login-success.component.css b/frontend/src/app/feature/login-success/login-success.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/app/feature/login-success/login-success.component.html b/frontend/src/app/feature/login-success/login-success.component.html deleted file mode 100644 index ba9d449..0000000 --- a/frontend/src/app/feature/login-success/login-success.component.html +++ /dev/null @@ -1 +0,0 @@ -

Logging in...

diff --git a/frontend/src/app/feature/login-success/login-success.component.ts b/frontend/src/app/feature/login-success/login-success.component.ts deleted file mode 100644 index bf81ac6..0000000 --- a/frontend/src/app/feature/login-success/login-success.component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ChangeDetectionStrategy, 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', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export default 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 = 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 deleted file mode 100644 index a579b7a..0000000 --- a/frontend/src/app/model/User.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface User { - keycloakId: string; - username: string; - balance: number; -} diff --git a/frontend/src/app/service/user.service.ts b/frontend/src/app/service/user.service.ts deleted file mode 100644 index ba6bead..0000000 --- a/frontend/src/app/service/user.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { KeycloakProfile } from 'keycloak-js'; -import { catchError, EMPTY, Observable } from 'rxjs'; -import { User } from '../model/User'; - -@Injectable({ - providedIn: 'root', -}) -export class UserService { - private http: HttpClient = inject(HttpClient); - - public getUser(id: string): Observable { - return this.http.get(`/backend/user/${id}`).pipe(catchError(() => EMPTY)); - } - - public createUser(id: string, username: string): Observable { - return this.http.post('/backend/user', { - keycloakId: id, - username: username, - }); - } - - public async getOrCreateUser(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(); - }); - } -} diff --git a/frontend/src/app/shared/components/footer/footer.component.html b/frontend/src/app/shared/components/footer/footer.component.html deleted file mode 100644 index 87c4682..0000000 --- a/frontend/src/app/shared/components/footer/footer.component.html +++ /dev/null @@ -1,80 +0,0 @@ -
-
-
- - - -
- -
- - - - - - - -
-
-
- -
-
- -
-
-
-
diff --git a/frontend/src/app/shared/components/footer/footer.component.ts b/frontend/src/app/shared/components/footer/footer.component.ts deleted file mode 100644 index 1c3b309..0000000 --- a/frontend/src/app/shared/components/footer/footer.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { faMoneyBillTransfer, faCreditCard, faWallet } from '@fortawesome/free-solid-svg-icons'; -import { faPaypal, faGooglePay, faApplePay } from '@fortawesome/free-brands-svg-icons'; - -@Component({ - selector: 'app-footer', - standalone: true, - templateUrl: './footer.component.html', - imports: [FontAwesomeModule], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FooterComponent { - currentYear: number = new Date().getFullYear(); - - faPaypal = faPaypal; - faCreditCard = faCreditCard; - faMoneyBillTransfer = faMoneyBillTransfer; - faWallet = faWallet; - faGooglePay = faGooglePay; - faApplePay = faApplePay; -} diff --git a/frontend/src/app/shared/components/navbar/navbar.component.html b/frontend/src/app/shared/components/navbar/navbar.component.html deleted file mode 100644 index fa492ae..0000000 --- a/frontend/src/app/shared/components/navbar/navbar.component.html +++ /dev/null @@ -1,70 +0,0 @@ - diff --git a/frontend/src/app/shared/components/navbar/navbar.component.ts b/frontend/src/app/shared/components/navbar/navbar.component.ts deleted file mode 100644 index 53d1dee..0000000 --- a/frontend/src/app/shared/components/navbar/navbar.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { KeycloakService } from 'keycloak-angular'; - -@Component({ - selector: 'app-navbar', - templateUrl: './navbar.component.html', - standalone: true, - imports: [RouterModule], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NavbarComponent { - isMenuOpen = false; - private keycloakService: KeycloakService = inject(KeycloakService); - - isLoggedIn = this.keycloakService.isLoggedIn(); - - login() { - try { - const baseUrl = window.location.origin; - this.keycloakService.login({ redirectUri: `${baseUrl}/login/success` }); - } catch (error) { - console.error('Login failed:', error); - } - } - - logout() { - this.keycloakService.logout(); - } - - toggleMenu() { - this.isMenuOpen = !this.isMenuOpen; - } -} diff --git a/frontend/src/index.html b/frontend/src/index.html index 8d60cc1..41fedf4 100644 --- a/frontend/src/index.html +++ b/frontend/src/index.html @@ -6,8 +6,13 @@ + + - + diff --git a/frontend/src/styles.css b/frontend/src/styles.css index f78bf74..12c08a1 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1,147 +1,19 @@ @import 'tailwindcss'; @theme { - --color-deep-blue: #0a1219; - --color-deep-blue-light: #121e27; - --color-deep-blue-contrast: #1a2835; - - --color-emerald: #10b981; - --color-emerald-dark: #059669; - --color-emerald-light: #34d399; - - --color-text-primary: #ffffff; - --color-text-secondary: #94a3b8; - --color-text-tertiary: #64748b; - - --color-accent-yellow: #fbbf24; - --color-accent-red: #ef4444; - --color-accent-purple: #8b5cf6; + --color-deep-blue: #0f212e; + --color-deep-blue-light: #1a2c38; + --color-deep-blue-contrast: #1b2c3b; + --color-light-blue: #1475e1; } body { - @apply bg-deep-blue text-text-primary h-full; -} - -button, -a { - @apply cursor-pointer active:scale-95 text-text-primary transition-all duration-200; -} - -.card { - @apply bg-deep-blue-contrast rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-shadow duration-300; -} - -.button-base { - @apply bg-emerald hover:bg-emerald-dark text-text-primary transition-all duration-300 active:scale-95 rounded; -} - -.game-card-content { - @apply p-4; -} - -.nav-button { - @apply hidden lg:block absolute top-1/2 -translate-y-1/2 bg-deep-blue-contrast hover:bg-deep-blue-contrast/90 text-text-primary p-3 rounded-full opacity-0 group-hover:opacity-100 transition-all duration-300 shadow-lg hover:scale-110; -} - -.slider-container { - @apply flex transition-transform duration-500 ease-out; -} - -.slider-grid { - @apply min-w-full grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4; -} - -.welcome-bonus { - @apply text-4xl sm:text-5xl lg:text-7xl font-extrabold text-emerald-light mb-3 sm:mb-4; -} - -.bonus-description { - @apply text-text-secondary text-base sm:text-lg mb-6 sm:mb-8; -} - -.section-heading { - @apply font-bold text-text-primary; -} - -.game-heading-sm { - @apply font-bold text-text-primary text-sm mb-2; -} - -.game-heading-xl { - @apply font-bold text-text-primary text-xl mb-2; -} - -.game-text { - @apply text-text-secondary text-sm mb-4; -} - -.stat-container { - @apply bg-deep-blue-contrast rounded-lg shadow-lg p-4 sm:p-6 text-center; -} - -.stat-number { - @apply text-xl sm:text-2xl font-bold text-emerald; -} - -.stat-text { - @apply text-text-secondary text-sm; -} - -.nav-brand { - @apply flex items-center text-text-primary text-xl font-semibold; -} - -.nav-link { - @apply px-3 py-2 rounded-md font-normal text-sm text-text-secondary hover:text-text-primary hover:bg-deep-blue-contrast transition-all duration-200; -} - -.nav-toggle { - @apply text-text-secondary hover:text-text-primary transition-colors duration-200; -} - -.nav-mobile-menu { - @apply p-2 pt-2 mb-4 space-y-1 bg-deep-blue-contrast rounded-b-lg; -} - -.nav-mobile-link { - @apply block px-3 py-2 rounded-md text-sm text-text-secondary hover:text-text-primary hover:bg-deep-blue-light transition-all duration-200; -} - -.footer-section { - @apply col-span-2 md:col-span-1; -} - -.footer-heading { - @apply text-text-primary text-sm font-semibold mb-4; -} - -.footer-link { - @apply text-text-secondary hover:text-text-primary text-sm transition-all duration-200; -} - -.footer-payment-method { - @apply bg-deep-blue rounded p-3 flex items-center justify-center space-x-2 hover:bg-deep-blue/50 transition-all duration-200; -} - -.footer-payment-icon { - @apply text-text-secondary text-lg; -} - -.footer-payment-text { - @apply text-text-secondary text-xs whitespace-nowrap; -} - -.footer-copyright { - @apply text-text-secondary text-sm; -} - -.footer-disclaimer { - @apply text-xs; + @apply bg-deep-blue text-gray-100; } .mat-mdc-dialog-container { - --mdc-dialog-container-color: var(--color-deep-blue-light) important; - --mdc-dialog-subhead-color: var(--color-text-primary) important; - --mdc-dialog-supporting-text-color: var(--color-text-secondary) important; - --mdc-dialog-container-shape: 6px important; + --mdc-dialog-container-color: var(--color-deep-blue-light) !important; + --mdc-dialog-subhead-color: #ffffff !important; + --mdc-dialog-supporting-text-color: #9ca3af !important; + --mdc-dialog-container-shape: 6px !important; }