Vlada645 5 дней назад
Родитель
Сommit
2098a84980
38 измененных файлов с 1563 добавлено и 150 удалено
  1. 33 0
      final project/database/pom.xml
  2. 52 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/JwtRequestFilter.java
  3. 37 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/JwtUtil.java
  4. 76 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/SecurityConfig.java
  5. 19 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/WebConfig.java
  6. 40 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/BugReportController.java
  7. 39 1
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/DeveloperController.java
  8. 153 5
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/GameController.java
  9. 72 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/LibraryController.java
  10. 41 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/OrderController.java
  11. 101 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/PaymentController.java
  12. 65 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/UserController.java
  13. 47 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/AuthResponse.java
  14. 30 5
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/BugReport.java
  15. 23 3
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Developer.java
  16. 52 2
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Game.java
  17. 33 11
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Library.java
  18. 15 23
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/LibraryItem.java
  19. 15 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/LoginRequest.java
  20. 20 28
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Order.java
  21. 27 34
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/User.java
  22. 60 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/UserProfileResponse.java
  23. 4 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/DeveloperRepository.java
  24. 3 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/LibraryItemRepository.java
  25. 3 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/OrderRepository.java
  26. 4 3
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/UserRepository.java
  27. 58 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/BugReportService.java
  28. 38 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/DeveloperService.java
  29. 75 3
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/GameService.java
  30. 90 0
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/LibraryService.java
  31. 73 17
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/OrderService.java
  32. 86 12
      final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/UserService.java
  33. 12 3
      final project/database/src/main/resources/application.properties
  34. 9 0
      final project/web-game-shop/src/app/models/developer.model.ts
  35. 14 0
      final project/web-game-shop/src/app/models/game.model.ts
  36. 6 0
      final project/web-game-shop/src/app/models/library.model.ts
  37. 13 0
      final project/web-game-shop/src/app/models/user.model.ts
  38. 25 0
      final project/web-game-shop/src/app/styles/tailwind.config.js

+ 33 - 0
final project/database/pom.xml

@@ -40,6 +40,39 @@
             <artifactId>spring-boot-starter-webmvc-test</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.12.6</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.12.6</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.12.6</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.stripe</groupId>
+            <artifactId>stripe-java</artifactId>
+            <version>28.0.0</version> </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-jpa</artifactId>

+ 52 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/JwtRequestFilter.java

@@ -0,0 +1,52 @@
+package pl.dmss.vmaneliuk.database.config;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+@Component
+public class JwtRequestFilter extends OncePerRequestFilter {
+
+    @Autowired
+    private JwtUtil jwtUtil;
+
+    @Override
+    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
+        String path = request.getRequestURI();
+        return path.equals("/api/users/register") || path.equals("/api/users/login");
+    }
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+            throws ServletException, IOException {
+
+        final String authorizationHeader = request.getHeader("Authorization");
+
+        String username = null;
+        String jwt = null;
+
+        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
+            jwt = authorizationHeader.substring(7);
+            username = jwtUtil.extractUsername(jwt);
+        }
+
+        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+            if (jwtUtil.validateToken(jwt)) {
+                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
+                        new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
+                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
+            }
+        }
+        filterChain.doFilter(request, response);
+    }
+}

+ 37 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/JwtUtil.java

@@ -0,0 +1,37 @@
+package pl.dmss.vmaneliuk.database.config;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.stereotype.Component;
+import java.security.Key;
+import java.util.Date;
+
+@Component
+public class JwtUtil {
+    private final String SECRET_STRING = "verySecretKeyThatShouldBeAtLeast32CharactersLong!!!";
+    private final Key key = Keys.hmacShaKeyFor(SECRET_STRING.getBytes());
+
+    public String generateToken(String username) {
+        return Jwts.builder()
+                .subject(username)
+                .issuedAt(new Date())
+                .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
+                .signWith(key)
+                .compact();
+    }
+
+
+    public String extractUsername(String token) {
+        return Jwts.parser().verifyWith((javax.crypto.SecretKey) key).build().parseSignedClaims(token).getPayload().getSubject();
+    }
+
+    public boolean validateToken(String token) {
+        try {
+            Jwts.parser().verifyWith((javax.crypto.SecretKey) key).build().parseSignedClaims(token);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}

+ 76 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/SecurityConfig.java

@@ -0,0 +1,76 @@
+package pl.dmss.vmaneliuk.database.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+    @Autowired
+    private JwtRequestFilter jwtRequestFilter;
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http
+                .csrf(csrf -> csrf.disable())
+                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+                .authorizeHttpRequests(auth -> auth
+                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+
+                        // Публічні шляхи авторизації та помилок
+                        .requestMatchers("/api/users/register", "/api/users/login", "/error").permitAll()
+
+                        // Дозволяємо перегляд ігор усім (GET)
+                        .requestMatchers(HttpMethod.GET, "/api/games/**").permitAll()
+                        .requestMatchers("/uploads/**").permitAll()
+
+                        // Операції з іграми (Створення, Редагування, Видалення) вимагають автентифікації!
+                        .requestMatchers(HttpMethod.POST, "/api/games/**").authenticated()
+                        .requestMatchers(HttpMethod.PUT, "/api/games/**").authenticated()
+                        .requestMatchers(HttpMethod.DELETE, "/api/games/**").authenticated()
+
+                        .requestMatchers("/api/library/**").permitAll()
+                        .requestMatchers("/api/orders/**").permitAll()
+
+                        // Решта запитів залишаються захищеними
+                        .anyRequest().authenticated()
+                )
+                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+                .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
+
+        return http.build();
+    }
+
+    @Bean
+    public CorsConfigurationSource corsConfigurationSource() {
+        CorsConfiguration configuration = new CorsConfiguration();
+        configuration.setAllowedOrigins(List.of("http://localhost:4200"));
+        configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+        configuration.setAllowedHeaders(List.of("*"));
+        configuration.setAllowCredentials(true);
+
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", configuration);
+        return source;
+    }
+}

+ 19 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/config/WebConfig.java

@@ -0,0 +1,19 @@
+package pl.dmss.vmaneliuk.database.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import java.nio.file.Paths;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        // Expose directory storage locally on server instance to external front requests
+        String uploadPath = Paths.get("uploads").toAbsolutePath().toUri().toString();
+
+        registry.addResourceHandler("/uploads/**")
+                .addResourceLocations(uploadPath);
+    }
+}

+ 40 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/BugReportController.java

@@ -0,0 +1,40 @@
+package pl.dmss.vmaneliuk.database.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.model.BugReport;
+import pl.dmss.vmaneliuk.database.services.BugReportService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/bugs")
+@CrossOrigin(origins = "http://localhost:4200")
+public class BugReportController {
+
+    private final BugReportService bugReportService;
+
+    @Autowired
+    public BugReportController(BugReportService bugReportService) {
+        this.bugReportService = bugReportService;
+    }
+
+    //  Endpoint for submitting a new bug request application
+    @PostMapping
+    public ResponseEntity<BugReport> reportBug(@RequestBody BugReport report) {
+        return ResponseEntity.ok(bugReportService.createReport(report));
+    }
+
+    //  Endpoint for testers and programmers to transition bug report life cycles
+    @PutMapping("/{id}/status")
+    public ResponseEntity<BugReport> updateStatus(@PathVariable Long id, @RequestParam String status) {
+        return ResponseEntity.ok(bugReportService.updateReportStatus(id, status));
+    }
+
+    //  Endpoint to view all reports on the IT management dashboard
+    @GetMapping
+    public ResponseEntity<List<BugReport>> getAllReports() {
+        return ResponseEntity.ok(bugReportService.getAllReports());
+    }
+}

+ 39 - 1
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/DeveloperController.java

@@ -1,5 +1,43 @@
 package pl.dmss.vmaneliuk.database.controllers;
 
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.model.Developer;
+import pl.dmss.vmaneliuk.database.services.DeveloperService;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/developers")
+@CrossOrigin(origins = "http://localhost:4200") // Allows Angular preflight CORS validation requests
 public class DeveloperController {
 
-}
+    private final DeveloperService developerService;
+
+    @Autowired
+    public DeveloperController(DeveloperService developerService) {
+        this.developerService = developerService;
+    }
+
+    @GetMapping
+    public ResponseEntity<List<Developer>> getAllDevelopers() {
+        return ResponseEntity.ok(developerService.getAllDevelopers());
+    }
+
+    @PostMapping
+    public ResponseEntity<Developer> createDeveloper(@RequestBody Developer developer) {
+        return ResponseEntity.ok(developerService.createDeveloper(developer));
+    }
+
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteDeveloper(@PathVariable("id") Long id) {
+        System.out.println("Received network instruction to purge developer studio ID: " + id);
+        try {
+            developerService.deleteDeveloper(id);
+            return ResponseEntity.noContent().build(); // Return HTTP 204 No Content upon success
+        } catch (Exception e) {
+            System.err.println("Execution fault during developer dropping procedure: " + e.getMessage());
+            return ResponseEntity.notFound().build(); // Return HTTP 404 if record missing
+        }
+    }
+}

+ 153 - 5
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/GameController.java

@@ -1,20 +1,63 @@
 package pl.dmss.vmaneliuk.database.controllers;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import pl.dmss.vmaneliuk.database.model.Developer;
 import pl.dmss.vmaneliuk.database.model.Game;
+import pl.dmss.vmaneliuk.database.model.User;
+import pl.dmss.vmaneliuk.database.model.BugReport;
 import pl.dmss.vmaneliuk.database.services.GameService;
+import pl.dmss.vmaneliuk.database.services.BugReportService;
 
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/api/games")
+@CrossOrigin(origins = "http://localhost:4200")
 public class GameController {
 
     private final GameService gameService;
 
-    public GameController(GameService gameService) {
+    private final BugReportService bugReportService;
+    private final String UPLOAD_DIRECTORY = "uploads";
+
+    @Autowired
+    public GameController(GameService gameService, BugReportService bugReportService) {
         this.gameService = gameService;
+        this.bugReportService = bugReportService;
+    }
+
+
+    @GetMapping("/{gameId}/reports")
+    public ResponseEntity<List<BugReport>> getReportsByGame(@PathVariable Long gameId) {
+        // Student Note: We grab all reports and filter out only those matching our gameId
+        List<BugReport> gameReports = bugReportService.getAllReports().stream()
+                .filter(report -> report.getGame() != null && report.getGame().getGameId().equals(gameId))
+                .collect(Collectors.toList());
+        return ResponseEntity.ok(gameReports);
+    }
+
+
+    @PutMapping("/{gameId}/reports/{reportId}/status")
+    public ResponseEntity<BugReport> updateReportStatusFromGameContext(
+            @PathVariable Long gameId,
+            @PathVariable Long reportId,
+            @RequestParam String status) {
+
+        BugReport updatedReport = bugReportService.updateReportStatus(reportId, status);
+        return ResponseEntity.ok(updatedReport);
     }
 
     @GetMapping
@@ -22,9 +65,114 @@ public class GameController {
         return ResponseEntity.ok(gameService.getAllGames());
     }
 
-    @PostMapping
-    public ResponseEntity<Game> createGame(@RequestBody Game game) {
-        Game savedGame = gameService.createGame(game);
-        return ResponseEntity.ok(savedGame);
+    @PostMapping(consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
+    public ResponseEntity<?> createGame(
+            @RequestPart("game") String gameJson,
+            @RequestPart(value = "image", required = false) MultipartFile file) {
+        try {
+            ObjectMapper objectMapper = new ObjectMapper();
+            objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
+
+            Game game = objectMapper.readValue(gameJson, Game.class);
+
+            if (game.getDeveloper() == null || game.getDeveloper().getDeveloperId() == null) {
+                Developer defaultDev = new Developer();
+                defaultDev.setDeveloperId(1L);
+                game.setDeveloper(defaultDev);
+            }
+
+            if (file != null && !file.isEmpty()) {
+                File directory = new File(UPLOAD_DIRECTORY);
+                if (!directory.exists()) {
+                    directory.mkdirs();
+                }
+
+                String uniqueFilename = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
+                Path filePath = Paths.get(UPLOAD_DIRECTORY, uniqueFilename);
+                Files.copy(file.getInputStream(), filePath);
+
+                game.setImagePath("/uploads/" + uniqueFilename);
+            }
+
+            Game savedGame = gameService.createGame(game);
+            return ResponseEntity.ok(savedGame);
+
+        } catch (Exception e) {
+            System.err.println("CRITICAL PERSISTENCE FAULT DETECTED:");
+            e.printStackTrace();
+            return ResponseEntity.status(500).body("Server internal mapping fault: " + e.getMessage());
+        }
+    }
+
+    @PutMapping(value = "/{id}", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
+    public ResponseEntity<Game> updateGame(
+            @PathVariable("id") Long id,
+            @RequestPart("game") String gameJson,
+            @RequestPart(value = "image", required = false) MultipartFile file) {
+        try {
+            ObjectMapper objectMapper = new ObjectMapper();
+            objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
+            objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+            Game updatedGame = objectMapper.readValue(gameJson, Game.class);
+            Game currentDatabaseState = gameService.getGameById(id)
+                    .orElseThrow(() -> new RuntimeException("Target record execution fault. Missing target link: " + id));
+
+            if (file != null && !file.isEmpty()) {
+                File directory = new File(UPLOAD_DIRECTORY);
+                if (!directory.exists()) directory.mkdirs();
+
+                String uniqueFilename = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
+                Path filePath = Paths.get(UPLOAD_DIRECTORY, uniqueFilename);
+                Files.copy(file.getInputStream(), filePath);
+
+                updatedGame.setImagePath("/uploads/" + uniqueFilename);
+            } else {
+                updatedGame.setImagePath(currentDatabaseState.getImagePath());
+            }
+
+            Game finalizedResult = gameService.updateGame(id, updatedGame);
+            return ResponseEntity.ok(finalizedResult);
+        } catch (Exception e) {
+            System.err.println("Execution fault inside alteration network sequence: " + e.getMessage());
+            e.printStackTrace();
+            return ResponseEntity.internalServerError().build();
+        }
+    }
+
+    @GetMapping("/{gameId}/developer")
+    public ResponseEntity<Developer> getGameDeveloper(@PathVariable Long gameId) {
+        return gameService.getGameById(gameId)
+                .map(game -> ResponseEntity.ok(game.getDeveloper()))
+                .orElse(ResponseEntity.notFound().build());
+    }
+
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteGame(@PathVariable("id") Long id) {
+        try {
+            gameService.deleteGame(id);
+            return ResponseEntity.noContent().build();
+        } catch (Exception e) {
+            return ResponseEntity.notFound().build();
+        }
+    }
+
+    @GetMapping("/{id}")
+    public ResponseEntity<Game> getGameById(@PathVariable Long id) {
+        return gameService.getGameById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+
+    @PostMapping("/{id}/buy")
+    public ResponseEntity<?> buyGame(@PathVariable Long id, @RequestParam Long userId) {
+        try {
+            User updatedUser = gameService.processGamePurchase(id, userId);
+            return ResponseEntity.ok(new pl.dmss.vmaneliuk.database.model.UserProfileResponse(updatedUser));
+        } catch (IllegalArgumentException e) {
+            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage(), "reason", "INSUFFICIENT_FUNDS"));
+        } catch (RuntimeException e) {
+            return ResponseEntity.status(404).body(Map.of("error", e.getMessage(), "reason", "NOT_FOUND"));
+        }
     }
 }

+ 72 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/LibraryController.java

@@ -0,0 +1,72 @@
+package pl.dmss.vmaneliuk.database.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.model.Library;
+import pl.dmss.vmaneliuk.database.model.LibraryItem;
+import pl.dmss.vmaneliuk.database.services.LibraryService;
+import pl.dmss.vmaneliuk.database.repository.LibraryRepository;
+
+@RestController
+@RequestMapping("/api/library")
+@CrossOrigin(origins = "http://localhost:4200")
+public class LibraryController {
+
+    private final LibraryService libraryService;
+    private final LibraryRepository libraryRepository;
+
+    @Autowired
+    public LibraryController(LibraryService libraryService, LibraryRepository libraryRepository) {
+        this.libraryService = libraryService;
+        this.libraryRepository = libraryRepository;
+    }
+
+    // Endpoint to manually assign a game platform record into a user library workspace
+    @PostMapping("/add")
+    public ResponseEntity<LibraryItem> addGame(@RequestParam Long userId, @RequestParam Long gameId) {
+        return ResponseEntity.ok(libraryService.addGameToLibrary(userId, gameId));
+    }
+
+    @PostMapping("/play/{itemId}")
+    public ResponseEntity<LibraryItem> playGame(@PathVariable Long itemId, @RequestParam Integer minutes) {
+        return ResponseEntity.ok(libraryService.launchGame(itemId, minutes));
+    }
+
+    @DeleteMapping("/refund")
+    public ResponseEntity<String> refundGame(@RequestParam Long userId, @RequestParam Long itemId) {
+        try {
+            libraryService.processGameRefund(userId, itemId);
+            return ResponseEntity.ok("Game refund processed successfully. Funds returned.");
+        } catch (RuntimeException e) {
+            return ResponseEntity.badRequest().body(e.getMessage());
+        }
+    }
+
+    @GetMapping("/user/{userId}")
+    public ResponseEntity<Library> getUserLibrary(@PathVariable Long userId) {
+        return libraryRepository.findAll().stream()
+                .filter(lib -> lib.getUser() != null && lib.getUser().getUserId().equals(userId))
+                .findFirst()
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+    @Autowired
+    private pl.dmss.vmaneliuk.database.repository.LibraryItemRepository libraryItemRepository;
+
+    @GetMapping("/user/{userId}/items")
+    public ResponseEntity<?> getLibraryItems(@PathVariable Long userId) {
+        Library library = libraryRepository.findAll().stream()
+                .filter(lib -> lib.getUser() != null && lib.getUser().getUserId().equals(userId))
+                .findFirst()
+                .orElse(null);
+
+        if (library == null) {
+            return ResponseEntity.notFound().build();
+        }
+
+        // Fetching all items belonging to the resolved library ID
+        java.util.List<LibraryItem> items = libraryItemRepository.findByLibrary_LibraryId(library.getLibraryId());
+        return ResponseEntity.ok(items);
+    }
+}

+ 41 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/OrderController.java

@@ -0,0 +1,41 @@
+package pl.dmss.vmaneliuk.database.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.model.Order;
+import pl.dmss.vmaneliuk.database.services.OrderService;
+import pl.dmss.vmaneliuk.database.repository.OrderRepository;
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/orders")
+@CrossOrigin(origins = "http://localhost:4200")
+public class OrderController {
+
+    private final OrderService orderService;
+    private final OrderRepository orderRepository;
+
+    @Autowired
+    public OrderController(OrderService orderService, OrderRepository orderRepository) {
+        this.orderService = orderService;
+        this.orderRepository = orderRepository;
+    }
+
+    @GetMapping
+    public ResponseEntity<List<Order>> getAllOrders() {
+        return ResponseEntity.ok(orderRepository.findAll());
+    }
+
+    @GetMapping("/history/{userId}")
+    public ResponseEntity<List<Order>> getUserOrderHistory(@PathVariable Long userId) {
+        return ResponseEntity.ok(orderService.getUserOrderHistory(userId));
+    }
+
+    @PostMapping("/purchase")
+    public ResponseEntity<Order> purchaseGame(@RequestParam Long userId, @RequestParam Long gameId) {
+        return ResponseEntity.ok(orderService.processGamePurchase(userId, gameId));
+    }
+
+
+}

+ 101 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/PaymentController.java

@@ -0,0 +1,101 @@
+package pl.dmss.vmaneliuk.database.controllers;
+
+import com.stripe.Stripe;
+import com.stripe.model.Event;
+import com.stripe.model.checkout.Session;
+import com.stripe.param.checkout.SessionCreateParams;
+import com.stripe.net.Webhook;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.services.UserService;
+
+import jakarta.annotation.PostConstruct;
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/payment")
+@CrossOrigin(origins = "http://localhost:4200", allowedHeaders = "*", methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})
+public class PaymentController {
+
+    @Value("${stripe.secret.key}")
+    private String stripeSecretKey;
+
+    private final UserService userService;
+
+    @Autowired
+    public PaymentController(UserService userService) {
+        this.userService = userService;
+    }
+
+    @PostConstruct
+    public void init() {
+        Stripe.apiKey = stripeSecretKey;
+    }
+
+    // Endpoint to generate a secure Stripe Checkout Session URL redirect link
+    @PostMapping("/create-checkout-session")
+    public ResponseEntity<?> createCheckoutSession(@RequestBody Map<String, Object> data) {
+        try {
+            Long userId = Long.valueOf(data.get("userId").toString());
+            double amount = Double.parseDouble(data.get("amount").toString());
+
+            // Stripe operates with structural cents (e.g., $10.00 is represented as 1000 cents)
+            long amountInCents = Math.round(amount * 100);
+
+            SessionCreateParams params = SessionCreateParams.builder()
+                    .setMode(SessionCreateParams.Mode.PAYMENT)
+                    .setSuccessUrl("http://localhost:4200/profile?status=success")
+                    .setCancelUrl("http://localhost:4200/profile?status=cancel")
+                    .addLineItem(
+                            SessionCreateParams.LineItem.builder()
+                                    .setQuantity(1L)
+                                    .setPriceData(
+                                            SessionCreateParams.LineItem.PriceData.builder()
+                                                    .setCurrency("usd")
+                                                    .setUnitAmount(amountInCents)
+                                                    .setProductData(
+                                                            SessionCreateParams.LineItem.PriceData.ProductData.builder()
+                                                                    .setName("Nexus Wallet Token Injection")
+                                                                    .setDescription("Adding funds to your node identity wallet")
+                                                                    .build()
+                                                    )
+                                                    .build()
+                                    )
+                                    .build()
+                    )
+                    // Passing system metadata attributes to correlate the customer entity inside the webhook callback
+                    .putMetadata("userId", String.valueOf(userId))
+                    .putMetadata("amount", String.valueOf(amount))
+                    .build();
+
+            Session session = Session.create(params);
+
+            Map<String, String> responseData = new HashMap<>();
+            responseData.put("url", session.getUrl());
+            return ResponseEntity.ok(responseData);
+
+        } catch (Exception e) {
+            System.err.println("Failed to initiate active Stripe intent sequence: " + e.getMessage());
+            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
+        }
+    }
+
+    // Local simulation endpoint for university review boundaries to bypass complex live proxy setups
+    @PostMapping("/simulate-success-webhook")
+    public ResponseEntity<?> simulateSuccessWebhook(@RequestBody Map<String, Object> payload) {
+        try {
+            Long userId = Long.valueOf(payload.get("userId").toString());
+            BigDecimal amount = new BigDecimal(payload.get("amount").toString());
+
+            // Reuse your existing verified service layer mapping logic directly
+            userService.addFundsToWallet(userId, amount);
+            return ResponseEntity.ok(Map.of("message", "Telemetry simulation update synced successfully into PostgreSQL node."));
+        } catch (Exception e) {
+            return ResponseEntity.badRequest().body(e.getMessage());
+        }
+    }
+}

+ 65 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/controllers/UserController.java

@@ -0,0 +1,65 @@
+package pl.dmss.vmaneliuk.database.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmss.vmaneliuk.database.model.User;
+import pl.dmss.vmaneliuk.database.services.UserService;
+import java.util.List;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/api/users")
+@CrossOrigin(origins = "http://localhost:4200")
+public class UserController {
+
+    private final UserService userService;
+
+    @Autowired
+    public UserController(UserService userService) {
+        this.userService = userService;
+    }
+
+    // Endpoint to register a single user object (Expects {} in Postman, NOT [])
+    @PostMapping("/register")
+    public ResponseEntity<User> registerUser(@RequestBody User user) {
+        return ResponseEntity.ok(userService.registerUser(user));
+    }
+
+    // Endpoint to verify user identities and dynamic routing parameters based on roles
+    @PostMapping("/login")
+    public ResponseEntity<?> loginUser(@RequestBody pl.dmss.vmaneliuk.database.model.LoginRequest loginRequest) {
+        return userService.authenticateUser(loginRequest.getEmail(), loginRequest.getPassword())
+                .<ResponseEntity<?>>map(ResponseEntity::ok)
+                .orElse(ResponseEntity.status(401).body("Invalid electronic mail or secure password configuration."));
+    }
+
+    //Endpoint to safely retrieve a complete list of users stored in the database
+    @GetMapping
+    public ResponseEntity<List<User>> getAllUsers() {
+        List<User> users = userService.getAllUsers();
+        return ResponseEntity.ok(users);
+    }
+
+    // Updated endpoint to return password-free operational profile telemetry using explicit DTO mappings
+    @GetMapping("/{id}")
+    public ResponseEntity<pl.dmss.vmaneliuk.database.model.UserProfileResponse> getUserProfile(@PathVariable Long id) {
+        return userService.getUserProfileById(id)
+                .map(ResponseEntity::ok)
+                .orElse(ResponseEntity.notFound().build());
+    }
+
+    // Endpoint to securely deposit operational tokens into specific user wallet nodes
+    @PutMapping("/{id}/add-funds")
+    public ResponseEntity<?> addFunds(@PathVariable Long id, @RequestParam java.math.BigDecimal amount) {
+        try {
+            User updatedUser = userService.addFundsToWallet(id, amount);
+            // Returning the password-free operational DTO mapping format to maintain alignment criteria
+            return ResponseEntity.ok(new pl.dmss.vmaneliuk.database.model.UserProfileResponse(updatedUser));
+        } catch (IllegalArgumentException e) {
+            return ResponseEntity.badRequest().body(e.getMessage());
+        } catch (RuntimeException e) {
+            return ResponseEntity.status(404).body(e.getMessage());
+        }
+    }
+}

+ 47 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/AuthResponse.java

@@ -0,0 +1,47 @@
+package pl.dmss.vmaneliuk.database.model;
+
+public class AuthResponse {
+    private Long userId;
+    private String token;
+    private String fullName;
+    private String role;
+
+    public AuthResponse(Long userId, String token, String fullName, String role) {
+        this.userId = userId;
+        this.token = token;
+        this.fullName = fullName;
+        this.role = role;
+    }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public String getToken() {
+        return token;
+    }
+
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    public String getFullName() {
+        return fullName;
+    }
+
+    public void setFullName(String fullName) {
+        this.fullName = fullName;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public void setRole(String role) {
+        this.role = role;
+    }
+}

+ 30 - 5
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/BugReport.java

@@ -11,11 +11,11 @@ public class BugReport {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long reportId;
 
-    @ManyToOne(fetch = FetchType.LAZY)
+    @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = "user_id", nullable = false)
     private User user;
 
-    @ManyToOne(fetch = FetchType.LAZY)
+    @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = "game_id", nullable = false)
     private Game game;
 
@@ -26,8 +26,33 @@ public class BugReport {
     private String bodyText;
 
     private String severityLevel;
-
     private String reportStatus;
-
     private LocalDateTime createdAt;
-}
+
+    public BugReport() {}
+
+    // Getters and Setters
+    public Long getReportId() { return reportId; }
+    public void setReportId(Long id) { this.reportId = id; }
+
+    public User getUser() { return user; }
+    public void setUser(User user) { this.user = user; }
+
+    public Game getGame() { return game; }
+    public void setGame(Game game) { this.game = game; }
+
+    public String getTitle() { return title; }
+    public void setTitle(String newTitle) { this.title = newTitle; }
+
+    public String getBodyText() { return bodyText; }
+    public void setBodyText(String bodyText) { this.bodyText = bodyText; }
+
+    public String getSeverityLevel() { return severityLevel; }
+    public void setSeverityLevel(String severityLevel) { this.severityLevel = severityLevel; }
+
+    public String getReportStatus() { return reportStatus; }
+    public void setReportStatus(String newStatus) { this.reportStatus = newStatus; }
+
+    public LocalDateTime getCreatedAt() { return createdAt; }
+    public void setCreatedAt(LocalDateTime now) { this.createdAt = now; }
+}

+ 23 - 3
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Developer.java

@@ -1,6 +1,7 @@
 package pl.dmss.vmaneliuk.database.model;
 
 import jakarta.persistence.*;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import java.util.List;
 
 @Entity
@@ -8,7 +9,7 @@ import java.util.List;
 public class Developer {
 
     @Id
-    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "developer_id")
     private Long developerId;
 
     @Column(nullable = false)
@@ -19,7 +20,26 @@ public class Developer {
 
     private String website;
 
-    // One developer can publish many games
     @OneToMany(mappedBy = "developer", cascade = CascadeType.ALL, orphanRemoval = true)
+    // Prevent infinite JSON recursion with games bidirectional association
+    @JsonIgnoreProperties("developer")
     private List<Game> games;
-}
+
+    public Developer() {}
+
+    // Getters and Setters
+    public Long getDeveloperId() { return developerId; }
+    public void setDeveloperId(Long id) { this.developerId = id; }
+
+    public String getStudioName() { return studioName; }
+    public void setStudioName(String studioName) { this.studioName = studioName; }
+
+    public String getContactEmail() { return contactEmail; }
+    public void setContactEmail(String contactEmail) { this.contactEmail = contactEmail; }
+
+    public String getWebsite() { return website; }
+    public void setWebsite(String website) { this.website = website; }
+
+    public List<Game> getGames() { return games; }
+    public void setGames(List<Game> games) { this.games = games; }
+}

+ 52 - 2
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Game.java

@@ -1,6 +1,8 @@
 package pl.dmss.vmaneliuk.database.model;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import jakarta.persistence.*;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
@@ -10,10 +12,14 @@ public class Game {
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "game_id")
+    @JsonProperty("gameId")
     private Long gameId;
 
-    @ManyToOne(fetch = FetchType.LAZY)
+    // Змініть LAZY на EAGER
+    @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = "developer_id", nullable = false)
+    @JsonIgnoreProperties("games")
     private Developer developer;
 
     @Column(nullable = false)
@@ -31,4 +37,48 @@ public class Game {
     private Integer reviewsTotal;
     private String tagsList;
     private LocalDateTime releaseDate;
-}
+    private String imagePath;
+
+    public Game() {}
+
+    // Getters and Setters
+    public Long getGameId() {
+        return this.gameId;
+    }
+    public void setGameId(Long gameId) {
+        this.gameId = gameId;
+    }
+    public Developer getDeveloper() { return this.developer; }
+    public void setDeveloper(Developer developer) { this.developer = developer; }
+
+    public String getTitle() { return title; }
+    public void setTitle(String title) { this.title = title; }
+
+    public String getDescription() { return description; }
+    public void setDescription(String description) { this.description = description; }
+
+    public BigDecimal getPrice() { return price; }
+    public void setPrice(BigDecimal price) { this.price = price; }
+
+    public BigDecimal getDiscountPercent() { return discountPercent; }
+    public void setDiscountPercent(BigDecimal discountPercent) { this.discountPercent = discountPercent; }
+
+    public String getVersion() { return version; }
+    public void setVersion(String version) { this.version = version; }
+
+    public Double getRatingScore() { return ratingScore; }
+    public void setRatingScore(Double ratingScore) { this.ratingScore = ratingScore; }
+
+    public Integer getReviewsTotal() { return reviewsTotal; }
+    public void setReviewsTotal(Integer reviewsTotal) { this.reviewsTotal = reviewsTotal; }
+
+    public String getTagsList() { return tagsList; }
+    public void setTagsList(String tagsList) { this.tagsList = tagsList; }
+
+    public LocalDateTime getReleaseDate() { return releaseDate; }
+    public void setReleaseDate(LocalDateTime releaseDate) { this.releaseDate = releaseDate; }
+
+    public String getImagePath() { return imagePath; }
+    public void setImagePath(String imagePath) { this.imagePath = imagePath; }
+
+}

+ 33 - 11
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Library.java

@@ -1,30 +1,52 @@
 package pl.dmss.vmaneliuk.database.model;
 
 import jakarta.persistence.*;
-
+import com.fasterxml.jackson.annotation.JsonBackReference;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
 import java.util.List;
+import java.util.ArrayList;
 
 @Entity
 @Table(name = "libraries")
 public class Library {
+
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long libraryId;
 
-    @OneToOne
-    @JoinColumn(name = "user_id", nullable = false)
+    @OneToOne(cascade = CascadeType.REFRESH)
+    @JoinColumn(name = "user_id", referencedColumnName = "userId", nullable = false)
+    @JsonBackReference
     private User user;
 
-    private Integer totalGameCount;
-    private Integer totalPlaytimeSum;
-    private float avgLibraryRating;
+    @OneToMany(mappedBy = "library", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+    @JsonManagedReference
+    private List<LibraryItem> libraryItems = new ArrayList<>();
+
+    @Column(name = "total_game_count", nullable = false)
+    private int totalGameCount = 0;
+
+    private Integer totalGamesCount;
+    private Long totalPlaytimeSum;
+    private double avgLibraryRating;
+
+    public Library() {}
+
+    public Long getLibraryId() { return libraryId; }
+    public void setLibraryId(Long libraryId) { this.libraryId = libraryId; }
+
+    public User getUser() { return user; }
+    public void setUser(User newUser) { this.user = newUser; }
 
-    public void setUser(User newUser) { user = newUser;}
+    public List<LibraryItem> getLibraryItems() { return libraryItems; }
+    public void setLibraryItems(List<LibraryItem> libraryItems) { this.libraryItems = libraryItems; }
 
-    public void setTotalGamesCount(int i) { totalGameCount = i;}
+    public Integer getTotalGameCount() { return totalGamesCount; }
+    public void setTotalGamesCount(int i) { this.totalGamesCount = i; }
 
-    public void setTotalPlaytimeSum(int i) { totalGameCount = i;}
+    public Long getTotalPlaytimeSum() { return totalPlaytimeSum; }
+    public void setTotalPlaytimeSum(long i) { this.totalPlaytimeSum = i; }
 
-    public void setAvgLibraryRating(float v) { avgLibraryRating = v;
-    }
+    public double getAvgLibraryRating() { return avgLibraryRating; }
+    public void setAvgLibraryRating(double v) { this.avgLibraryRating = v; }
 }

+ 15 - 23
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/LibraryItem.java

@@ -1,6 +1,7 @@
 package pl.dmss.vmaneliuk.database.model;
 
 import jakarta.persistence.*;
+import com.fasterxml.jackson.annotation.JsonBackReference;
 import java.time.LocalDateTime;
 
 @Entity
@@ -13,6 +14,7 @@ public class LibraryItem {
 
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "library_id", nullable = false)
+    @JsonBackReference
     private Library library;
 
     @ManyToOne(fetch = FetchType.LAZY)
@@ -25,33 +27,23 @@ public class LibraryItem {
 
     private String ownershipType;
 
-    // Default constructor (required by JPA)
-    public LibraryItem() {
-    }
+    public LibraryItem() {}
 
-    // Getters and Setters
+    public Long getItemId() { return itemId; }
+    public void setItemId(Long itemId) { this.itemId = itemId; }
 
-    public Long getItemId() {return itemId;}
+    public Library getLibrary() { return library; }
+    public void setLibrary(Library library) { this.library = library; }
 
-    public void setItemId(Long itemId) {this.itemId = itemId;}
+    public Game getGame() { return game; }
+    public void setGame(Game game) { this.game = game; }
 
-    public Library getLibrary() {return library;}
+    public Integer getPlaytimeMinutes() { return playtimeMinutes; }
+    public void setPlaytimeMinutes(Integer playtimeMinutes) { this.playtimeMinutes = playtimeMinutes; }
 
-    public void setLibrary(Library library) {this.library = library;}
+    public LocalDateTime getLastPlayedAt() { return lastPlayedAt; }
+    public void setLastPlayedAt(LocalDateTime lastPlayedAt) { this.lastPlayedAt = lastPlayedAt; }
 
-    public Game getGame() {return game;}
-
-    public void setGame(Game game) {this.game = game;}
-
-    public Integer getPlaytimeMinutes() {return playtimeMinutes;}
-
-    public void setPlaytimeMinutes(Integer playtimeMinutes) {this.playtimeMinutes = playtimeMinutes;}
-
-    public LocalDateTime getLastPlayedAt() {return lastPlayedAt;}
-
-    public void setLastPlayedAt(LocalDateTime lastPlayedAt) {this.lastPlayedAt = lastPlayedAt;}
-
-    public String getOwnershipType() {return ownershipType;}
-
-    public void setOwnershipType(String ownershipType) {this.ownershipType = ownershipType;}
+    public String getOwnershipType() { return ownershipType; }
+    public void setOwnershipType(String ownershipType) { this.ownershipType = ownershipType; }
 }

+ 15 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/LoginRequest.java

@@ -0,0 +1,15 @@
+package pl.dmss.vmaneliuk.database.model;
+
+public class LoginRequest {
+    private String email;
+    private String password;
+
+    public LoginRequest() {}
+
+    // Getters and Setters mapped for strict JSON transmission binding
+    public String getEmail() { return email; }
+    public void setEmail(String email) { this.email = email; }
+
+    public String getPassword() { return password; }
+    public void setPassword(String password) { this.password = password; }
+}

+ 20 - 28
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/Order.java

@@ -1,6 +1,7 @@
 package pl.dmss.vmaneliuk.database.model;
 
 import jakarta.persistence.*;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
@@ -12,11 +13,12 @@ public class Order {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long orderId;
 
-    @ManyToOne(fetch = FetchType.LAZY)
+    @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = "user_id", nullable = false)
+    @JsonIgnore
     private User user;
 
-    @ManyToOne(fetch = FetchType.LAZY)
+    @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = "game_id", nullable = false)
     private Game game;
 
@@ -34,40 +36,30 @@ public class Order {
     @Column(nullable = false)
     private LocalDateTime orderDate;
 
-    // Default constructor (required by JPA)
     public Order() {}
 
-    // Getters and Setters
+    public Long getOrderId() { return orderId; }
+    public void setOrderId(Long orderId) { this.orderId = orderId; }
 
-    public Long getOrderId() {return orderId;}
+    public User getUser() { return user; }
+    public void setUser(User user) { this.user = user; }
 
-    public void setOrderId(Long orderId) {this.orderId = orderId;}
+    public Game getGame() { return game; }
+    public void setGame(Game game) { this.game = game; }
 
-    public User getUser() {return user;}
+    public BigDecimal getSubtotal() { return subtotal; }
+    public void setSubtotal(BigDecimal subtotal) { this.subtotal = subtotal; }
 
-    public void setUser(User user) {this.user = user;}
+    public BigDecimal getDiscountApplied() { return discountApplied; }
+    public void setDiscountApplied(BigDecimal discountApplied) { this.discountApplied = discountApplied; }
 
-    public Game getGame() {return game;}
+    public BigDecimal getFinalPrice() { return finalPrice; }
+    public void setFinalPrice(BigDecimal finalPrice) { this.finalPrice = finalPrice; }
 
-    public void setGame(Game game) {this.game = game;}
+    public String getPaymentStatus() { return paymentStatus; }
+    public void setPaymentStatus(String paymentStatus) { this.paymentStatus = paymentStatus; }
 
-    public BigDecimal getSubtotal() {return subtotal;}
+    public LocalDateTime getOrderDate() { return orderDate; }
 
-    public void setSubtotal(BigDecimal subtotal) {this.subtotal = subtotal;}
-
-    public BigDecimal getDiscountApplied() {return discountApplied;}
-
-    public void setDiscountApplied(BigDecimal discountApplied) {this.discountApplied = discountApplied;}
-
-    public BigDecimal getFinalPrice() {return finalPrice;}
-
-    public void setFinalPrice(BigDecimal finalPrice) {this.finalPrice = finalPrice;}
-
-    public String getPaymentStatus() {return paymentStatus;}
-
-    public void setPaymentStatus(String paymentStatus) {this.paymentStatus = paymentStatus;}
-
-    public LocalDateTime getOrderDate() {return orderDate;}
-
-    public void setOrderDate(LocalDateTime orderDate) {this.orderDate = orderDate;}
+    public void setOrderDate(LocalDateTime orderDate) { this.orderDate = orderDate; }
 }

+ 27 - 34
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/User.java

@@ -1,6 +1,8 @@
 package pl.dmss.vmaneliuk.database.model;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import jakarta.persistence.*;
+import com.fasterxml.jackson.annotation.JsonManagedReference;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
@@ -12,61 +14,52 @@ public class User {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long userId;
 
-    @Column(nullable = false)
     private String fullName;
-
     @Column(nullable = false, unique = true)
     private String email;
-
-    @Column(nullable = false)
     private String passwordHash;
-
-    private String accountStatus;
+    private String role;
     private LocalDateTime memberSince;
+    private String accountStatus;
     private BigDecimal walletBalance;
     private BigDecimal totalSpent;
 
-    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+
+    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+    @JsonManagedReference
     private Library library;
 
-    // Default constructor (required by JPA)
     public User() {}
 
     // Getters and Setters
 
-    public Long getUserId() {return userId;}
-
-    public void setUserId(Long userId) {this.userId = userId;}
-
-    public String getFullName() {return fullName;}
-
-    public void setFullName(String fullName) {this.fullName = fullName;}
-
-    public String getEmail() {return email;}
-
-    public void setEmail(String email) {this.email = email;}
-
-    public String getPasswordHash() {return passwordHash;}
-
-    public void setPasswordHash(String passwordHash) {this.passwordHash = passwordHash;}
-
-    public String getAccountStatus() {return accountStatus;}
+    public Long getUserId() { return userId; }
+    public void setUserId(Long userId) { this.userId = userId; }
 
-    public void setAccountStatus(String accountStatus) {this.accountStatus = accountStatus;}
+    public String getFullName() { return fullName; }
+    public void setFullName(String fullName) { this.fullName = fullName; }
 
-    public LocalDateTime getMemberSince() {return memberSince;}
+    public String getEmail() { return email; }
+    public void setEmail(String email) { this.email = email; }
 
-    public void setMemberSince(LocalDateTime memberSince) {this.memberSince = memberSince;}
+    public String getPasswordHash() { return passwordHash; }
+    public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
 
-    public BigDecimal getWalletBalance() {return walletBalance;}
+    public String getRole() { return role; }
+    public void setRole(String role) { this.role = role; }
 
-    public void setWalletBalance(BigDecimal walletBalance) {this.walletBalance = walletBalance;}
+    public LocalDateTime getMemberSince() { return memberSince; }
+    public void setMemberSince(LocalDateTime memberSince) { this.memberSince = memberSince; }
 
-    public BigDecimal getTotalSpent() {return totalSpent;}
+    public String getAccountStatus() { return accountStatus; }
+    public void setAccountStatus(String accountStatus) { this.accountStatus = accountStatus; }
 
-    public void setTotalSpent(BigDecimal totalSpent) {this.totalSpent = totalSpent;}
+    public BigDecimal getWalletBalance() { return walletBalance; }
+    public void setWalletBalance(BigDecimal walletBalance) { this.walletBalance = walletBalance; }
 
-    public Library getLibrary() {return library;}
+    public BigDecimal getTotalSpent() { return totalSpent; }
+    public void setTotalSpent(BigDecimal totalSpent) { this.totalSpent = totalSpent; }
 
-    public void setLibrary(Library library) {this.library = library;}
+    public Library getLibrary() { return library; }
+    public void setLibrary(Library library) { this.library = library; }
 }

+ 60 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/model/UserProfileResponse.java

@@ -0,0 +1,60 @@
+package pl.dmss.vmaneliuk.database.model;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public class UserProfileResponse {
+    private Long userId;
+    private String fullName;
+    private String email;
+    private String role;
+    private LocalDateTime memberSince;
+    private String accountStatus;
+    private BigDecimal walletBalance;
+    private BigDecimal totalSpent;
+    private LibrarySummary library;
+
+    // Inner static class to transfer streamlined relational gaming data structures safely
+    public static class LibrarySummary {
+        private Integer totalGamesCount;
+        private Integer totalPlaytimeSum;
+
+        public LibrarySummary(Integer totalGamesCount, Integer totalPlaytimeSum) {
+            this.totalGamesCount = totalGamesCount;
+            this.totalPlaytimeSum = totalPlaytimeSum;
+        }
+
+        public Integer getTotalGamesCount() { return totalGamesCount; }
+        public Integer getTotalPlaytimeSum() { return totalPlaytimeSum; }
+    }
+
+    public UserProfileResponse(pl.dmss.vmaneliuk.database.model.User user) {
+        this.userId = user.getUserId();
+        this.fullName = user.getFullName();
+        this.email = user.getEmail();
+        this.role = user.getRole();
+        this.memberSince = user.getMemberSince();
+        this.accountStatus = user.getAccountStatus();
+        this.walletBalance = user.getWalletBalance();
+        this.totalSpent = user.getTotalSpent();
+
+        // Safely extracting lazy relational elements inside transactional boundary processing
+        if (user.getLibrary() != null) {
+            this.library = new LibrarySummary(
+                    user.getLibrary().getTotalGameCount(),
+                    Math.toIntExact(user.getLibrary().getTotalPlaytimeSum())
+            );
+        }
+    }
+
+    // Standard public component getters for JSON mapping operations
+    public Long getUserId() { return userId; }
+    public String getFullName() { return fullName; }
+    public String getEmail() { return email; }
+    public String getRole() { return role; }
+    public LocalDateTime getMemberSince() { return memberSince; }
+    public String getAccountStatus() { return accountStatus; }
+    public BigDecimal getWalletBalance() { return walletBalance; }
+    public BigDecimal getTotalSpent() { return totalSpent; }
+    public LibrarySummary getLibrary() { return library; }
+}

+ 4 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/DeveloperRepository.java

@@ -3,7 +3,11 @@ package pl.dmss.vmaneliuk.database.repository;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import pl.dmss.vmaneliuk.database.model.Developer;
+import pl.dmss.vmaneliuk.database.model.Game;
+
+import java.util.List;
 
 @Repository
 public interface DeveloperRepository extends JpaRepository<Developer, Long> {
+    List<Game> findByDeveloperId(Long developerId);
 }

+ 3 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/LibraryItemRepository.java

@@ -4,6 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import pl.dmss.vmaneliuk.database.model.LibraryItem;
 
+import java.util.List;
+
 @Repository
 public interface LibraryItemRepository extends JpaRepository<LibraryItem, Long> {
+    List<LibraryItem> findByLibrary_LibraryId(Long libraryId);
 }

+ 3 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/OrderRepository.java

@@ -4,6 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import pl.dmss.vmaneliuk.database.model.Order;
 
+import java.util.List;
+
 @Repository
 public interface OrderRepository extends JpaRepository<Order, Long> {
+    List<Order> findByUserUserId(Long userId);
 }

+ 4 - 3
final project/database/src/main/java/pl/dmss/vmaneliuk/database/repository/UserRepository.java

@@ -1,11 +1,12 @@
 package pl.dmss.vmaneliuk.database.repository;
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
 import pl.dmss.vmaneliuk.database.model.User;
-
 import java.util.Optional;
 
+@Repository
 public interface UserRepository extends JpaRepository<User, Long> {
+    // Custom query method wrapper to seek existing credentials during login pipeline
     Optional<User> findByEmail(String email);
-
-}
+}

+ 58 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/BugReportService.java

@@ -0,0 +1,58 @@
+package pl.dmss.vmaneliuk.database.services;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.dmss.vmaneliuk.database.model.BugReport;
+import pl.dmss.vmaneliuk.database.model.Game;
+import pl.dmss.vmaneliuk.database.model.User;
+import pl.dmss.vmaneliuk.database.repository.BugReportRepository;
+import pl.dmss.vmaneliuk.database.repository.GameRepository;
+import pl.dmss.vmaneliuk.database.repository.UserRepository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+public class BugReportService {
+
+    private final BugReportRepository bugReportRepository;
+    private final UserRepository userRepository;
+    private final GameRepository gameRepository;
+
+    @Autowired
+    //  Added missing repository layer components into constructor to fetch full validation profiles
+    public BugReportService(BugReportRepository bugReportRepository, UserRepository userRepository, GameRepository gameRepository) {
+        this.bugReportRepository = bugReportRepository;
+        this.userRepository = userRepository;
+        this.gameRepository = gameRepository;
+    }
+
+    @Transactional
+    public BugReport createReport(BugReport report) {
+        //  Fetch managed entities to fully satisfy non-null database constraints during persistence lifecycle
+        User managedUser = userRepository.findById(report.getUser().getUserId())
+                .orElseThrow(() -> new RuntimeException("Reporting user profile not found"));
+        Game managedGame = gameRepository.findById(report.getGame().getGameId())
+                .orElseThrow(() -> new RuntimeException("Targeted game platform listing not found"));
+
+        report.setUser(managedUser);
+        report.setGame(managedGame);
+        report.setCreatedAt(LocalDateTime.now());
+        report.setReportStatus("OPEN");
+
+        return bugReportRepository.save(report);
+    }
+
+    @Transactional
+    public BugReport updateReportStatus(Long reportId, String newStatus) {
+        BugReport report = bugReportRepository.findById(reportId)
+                .orElseThrow(() -> new RuntimeException("Bug report logging entity not found"));
+        report.setReportStatus(newStatus);
+        return bugReportRepository.save(report);
+    }
+
+    public List<BugReport> getAllReports() {
+        return bugReportRepository.findAll();
+    }
+}

+ 38 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/DeveloperService.java

@@ -0,0 +1,38 @@
+package pl.dmss.vmaneliuk.database.services;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.dmss.vmaneliuk.database.model.Developer;
+import pl.dmss.vmaneliuk.database.repository.DeveloperRepository;
+
+import java.util.List;
+
+@Service
+public class DeveloperService {
+
+    private final DeveloperRepository developerRepository;
+
+    @Autowired
+    public DeveloperService(DeveloperRepository developerRepository) {
+        this.developerRepository = developerRepository;
+    }
+
+    public List<Developer> getAllDevelopers() {
+        return developerRepository.findAll();
+    }
+
+    @Transactional
+    public Developer createDeveloper(Developer developer) {
+        return developerRepository.save(developer);
+    }
+
+    @Transactional
+    public void deleteDeveloper(Long developerId) {
+        if (developerRepository.existsById(developerId)) {
+            developerRepository.deleteById(developerId);
+        } else {
+            throw new RuntimeException("Developer studio profile not found with ID: " + developerId);
+        }
+    }
+}

+ 75 - 3
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/GameService.java

@@ -1,27 +1,99 @@
 package pl.dmss.vmaneliuk.database.services;
 
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import pl.dmss.vmaneliuk.database.model.Game;
+import pl.dmss.vmaneliuk.database.model.User;
 import pl.dmss.vmaneliuk.database.repository.GameRepository;
 
 import java.util.List;
+import java.util.Optional;
 
 @Service
 public class GameService {
 
     private final GameRepository gameRepository;
 
-    // Dependency injection via constructor
+    @Autowired
     public GameService(GameRepository gameRepository) {
         this.gameRepository = gameRepository;
     }
 
+
     public List<Game> getAllGames() {
         return gameRepository.findAll();
     }
 
+    public Optional<Game> getGameById(Long id) {
+        return gameRepository.findById(id);
+    }
+
+    @Transactional
     public Game createGame(Game game) {
-        // Business logic could go here before saving
         return gameRepository.save(game);
     }
-}
+
+    // New service operation to modify an existing database record safely
+    @Transactional
+    public Game updateGame(Long id, Game updatedGame) {
+        return gameRepository.findById(id)
+                .map(existingGame -> {
+                    existingGame.setTitle(updatedGame.getTitle());
+                    existingGame.setDescription(updatedGame.getDescription());
+                    existingGame.setPrice(updatedGame.getPrice());
+                    existingGame.setDiscountPercent(updatedGame.getDiscountPercent());
+                    existingGame.setVersion(updatedGame.getVersion());
+                    existingGame.setTagsList(updatedGame.getTagsList());
+                    existingGame.setDeveloper(updatedGame.getDeveloper());
+
+                    // Only overwrite image parameters if a new media pointer is explicitly passed
+                    if (updatedGame.getImagePath() != null) {
+                        existingGame.setImagePath(updatedGame.getImagePath());
+                    }
+
+                    return gameRepository.save(existingGame);
+                })
+                .orElseThrow(() -> new RuntimeException("Game records mismatch. Id not found: " + id));
+    }
+
+    @Transactional
+    public void deleteGame(Long gameId) {
+        if (gameRepository.existsById(gameId)) {
+            gameRepository.deleteById(gameId);
+        } else {
+            throw new RuntimeException("Game not found with id: " + gameId);
+        }
+    }
+    @Autowired
+    private pl.dmss.vmaneliuk.database.repository.UserRepository userRepository;
+    @Transactional
+    public User processGamePurchase(Long gameId, Long userId) {
+        Game game = gameRepository.findById(gameId)
+                .orElseThrow(() -> new RuntimeException("Game sequence resource missing matching reference: " + gameId));
+
+        pl.dmss.vmaneliuk.database.model.User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User identity entity not found on active database nodes: " + userId));
+
+        // Calculate final checkout price taking historical discounts into context
+        java.math.BigDecimal finalPrice = game.getPrice();
+        if (game.getDiscountPercent() != null && game.getDiscountPercent().compareTo(java.math.BigDecimal.ZERO) > 0) {
+            java.math.BigDecimal discountFactor = java.math.BigDecimal.valueOf(100).subtract(game.getDiscountPercent());
+            finalPrice = finalPrice.multiply(discountFactor).divide(java.math.BigDecimal.valueOf(100), 2, java.math.RoundingMode.HALF_UP);
+        }
+
+        // Boundary assertion check: Verify wallet token balances can cover price parameters
+        if (user.getWalletBalance().compareTo(finalPrice) < 0) {
+            throw new IllegalArgumentException("Transaction aborted. Insufficient financial tokens inside identity wallet node.");
+        }
+
+        // Deduct operational price from customer node wallet pool
+        user.setWalletBalance(user.getWalletBalance().subtract(finalPrice));
+
+        // Accumulate historical spending matrix
+        if (user.getTotalSpent() == null) user.setTotalSpent(java.math.BigDecimal.ZERO);
+        user.setTotalSpent(user.getTotalSpent().add(finalPrice));
+
+        return userRepository.save(user);
+    }
+}

+ 90 - 0
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/LibraryService.java

@@ -0,0 +1,90 @@
+package pl.dmss.vmaneliuk.database.services;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.dmss.vmaneliuk.database.model.*;
+import pl.dmss.vmaneliuk.database.repository.*;
+
+import java.time.LocalDateTime;
+
+@Service
+public class LibraryService {
+
+    private final LibraryItemRepository libraryItemRepository;
+    private final UserRepository userRepository;
+    private final GameRepository gameRepository;
+
+    @Autowired
+    public LibraryService(LibraryItemRepository libraryItemRepository, UserRepository userRepository, GameRepository gameRepository) {
+        this.libraryItemRepository = libraryItemRepository;
+        this.userRepository = userRepository;
+        this.gameRepository = gameRepository;
+    }
+
+    @Transactional
+    public LibraryItem addGameToLibrary(Long userId, Long gameId) {
+        // Load user and game entities first
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User not found"));
+        Game game = gameRepository.findById(gameId)
+                .orElseThrow(() -> new RuntimeException("Game not found"));
+
+        // Null safety check for wallet balance
+        if (user.getWalletBalance() == null) {
+            user.setWalletBalance(java.math.BigDecimal.ZERO);
+        }
+
+        // Validate wallet balance threshold
+        if (user.getWalletBalance().compareTo(game.getPrice()) < 0) {
+            throw new RuntimeException("Insufficient wallet balance metrics to acquire this digital license.");
+        }
+
+        // Ensure user library is initialized
+        if (user.getLibrary() == null) {
+            throw new RuntimeException("Target user profile lacks an initialized library asset framework.");
+        }
+
+        LibraryItem item = new LibraryItem();
+        item.setLibrary(user.getLibrary());
+        item.setGame(game);
+        item.setPlaytimeMinutes(0);
+        item.setOwnershipType("MANUAL_ADD");
+        item.setLastPlayedAt(LocalDateTime.now());
+
+        // Deduct funds and persist user state changes
+        user.setWalletBalance(user.getWalletBalance().subtract(game.getPrice()));
+        userRepository.save(user);
+
+        return libraryItemRepository.save(item);
+    }
+
+    @Transactional
+    public LibraryItem launchGame(Long itemId, Integer minutesPlayed) {
+        LibraryItem item = libraryItemRepository.findById(itemId)
+                .orElseThrow(() -> new RuntimeException("Game not found inside user library inventory"));
+
+        if (item.getPlaytimeMinutes() == null) {
+            item.setPlaytimeMinutes(0);
+        }
+        item.setPlaytimeMinutes(item.getPlaytimeMinutes() + minutesPlayed);
+        item.setLastPlayedAt(LocalDateTime.now());
+        return libraryItemRepository.save(item);
+    }
+
+    @Transactional
+    public void processGameRefund(Long userId, Long itemId) {
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User profile not found"));
+        LibraryItem item = libraryItemRepository.findById(itemId)
+                .orElseThrow(() -> new RuntimeException("Library asset not found"));
+
+        Game game = item.getGame();
+
+        user.setWalletBalance(user.getWalletBalance().add(game.getPrice()));
+        user.setTotalSpent(user.getTotalSpent().subtract(game.getPrice()));
+
+        userRepository.save(user);
+        libraryItemRepository.delete(item);
+    }
+}

+ 73 - 17
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/OrderService.java

@@ -1,47 +1,103 @@
 package pl.dmss.vmaneliuk.database.services;
 
-import pl.dmss.vmaneliuk.database.model.*;
-import pl.dmss.vmaneliuk.database.repository.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.server.ResponseStatusException;
+import pl.dmss.vmaneliuk.database.model.*;
+import pl.dmss.vmaneliuk.database.repository.*;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Service
 public class OrderService {
 
     private final OrderRepository orderRepository;
     private final UserRepository userRepository;
+    private final GameRepository gameRepository;
     private final LibraryItemRepository libraryItemRepository;
 
-    public OrderService(OrderRepository orderRepository, UserRepository userRepository, LibraryItemRepository libraryItemRepository) {
+    @Autowired
+    public OrderService(OrderRepository orderRepository, UserRepository userRepository,
+                        GameRepository gameRepository, LibraryItemRepository libraryItemRepository) {
         this.orderRepository = orderRepository;
         this.userRepository = userRepository;
+        this.gameRepository = gameRepository;
         this.libraryItemRepository = libraryItemRepository;
     }
 
     @Transactional
-    public Order placeOrder(User user, Game game, Order order) {
-        // 1. Update user's wallet balance and spending history
-        user.setWalletBalance(user.getWalletBalance().subtract(order.getFinalPrice()));
-        user.setTotalSpent(user.getTotalSpent().add(order.getFinalPrice()));
+    public Order processGamePurchase(Long userId, Long gameId) {
+        // Fetch user and game entities from database repositories
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User node registry entry not found"));
+        Game game = gameRepository.findById(gameId)
+                .orElseThrow(() -> new RuntimeException("Game asset registry entry not found"));
+
+        if (user.getLibrary() != null && user.getLibrary().getLibraryItems() != null) {
+            boolean alreadyOwned = user.getLibrary().getLibraryItems().stream()
+                    .anyMatch(item -> item != null && item.getGame() != null && gameId.equals(item.getGame().getGameId()));
+
+            if (alreadyOwned) {
+                // Throwing inline exception directly to trigger immediate browser response layout mapping
+                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "ALREADY_OWNED");
+            }
+        }
+
+        // Safeguard against uninitialized database null values inside the entity node
+        if (user.getWalletBalance() == null) {
+            user.setWalletBalance(BigDecimal.ZERO);
+        }
+
+        BigDecimal priceToCharge = game.getPrice();
+
+        // Validation boundary check: Ensure wallet can cover transaction requirements
+        if (user.getWalletBalance().compareTo(priceToCharge) < 0) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Insufficient funds");
+        }
+
+        // Deduct transaction parameters from core wallet entity field allocation
+        user.setWalletBalance(user.getWalletBalance().subtract(priceToCharge));
+
+        // CRITICAL BUGFIX: Increment dynamic lifetime monetary investment tracking node
+        if (user.getTotalSpent() == null) {
+            user.setTotalSpent(priceToCharge);
+        } else {
+            user.setTotalSpent(user.getTotalSpent().add(priceToCharge));
+        }
+
+        // Flush and synchronize user wallet and spent metrics changes into PostgreSQL layout
         userRepository.save(user);
 
-        // 2. Save the order details
+        // Build operational order ledger confirmation reference mapping matching your Order entity columns
+        Order order = new Order();
         order.setUser(user);
         order.setGame(game);
-        order.setOrderDate(LocalDateTime.now());
+        order.setSubtotal(priceToCharge);
+        order.setDiscountApplied(BigDecimal.ZERO);
+        order.setFinalPrice(priceToCharge);
         order.setPaymentStatus("COMPLETED");
+        order.setOrderDate(LocalDateTime.now());
+
+        // Save order first to get the record established in the ledger
         Order savedOrder = orderRepository.save(order);
 
-        // 3. Add the game to the user's library
-        LibraryItem item = new LibraryItem();
-        item.setLibrary((Library) user.getLibrary());
-        item.setGame(game);
-        item.setPlaytimeMinutes(0);
-        item.setLastPlayedAt(null);
-        item.setOwnershipType("PURCHASED");
-        libraryItemRepository.save(item);
+        // FEATURE ALLOCATION: Automatically assign the digital license item to the user's library mapping layout
+        LibraryItem libraryItem = new LibraryItem();
+        libraryItem.setLibrary(user.getLibrary());
+        libraryItem.setGame(game);
+        libraryItem.setPlaytimeMinutes(0);
+        libraryItem.setOwnershipType("PURCHASED");
+        libraryItem.setLastPlayedAt(LocalDateTime.now());
+
+        libraryItemRepository.save(libraryItem);
 
         return savedOrder;
     }
+
+    public List<Order> getUserOrderHistory(Long userId) {
+        return orderRepository.findByUserUserId(userId);
+    }
 }

+ 86 - 12
final project/database/src/main/java/pl/dmss/vmaneliuk/database/services/UserService.java

@@ -1,43 +1,117 @@
 package pl.dmss.vmaneliuk.database.services;
 
-import jakarta.transaction.Transactional;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
-import pl.dmss.vmaneliuk.database.model.Library;
+import org.springframework.transaction.annotation.Transactional;
+import pl.dmss.vmaneliuk.database.config.JwtUtil;
+import pl.dmss.vmaneliuk.database.model.AuthResponse;
+import pl.dmss.vmaneliuk.database.model.Developer;
 import pl.dmss.vmaneliuk.database.model.User;
+import pl.dmss.vmaneliuk.database.model.Library;
+import pl.dmss.vmaneliuk.database.repository.DeveloperRepository;
 import pl.dmss.vmaneliuk.database.repository.UserRepository;
+import pl.dmss.vmaneliuk.database.repository.LibraryRepository;
 
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.Optional;
 
 @Service
 public class UserService {
+
     private final UserRepository userRepository;
+    private final LibraryRepository libraryRepository;
+    private final DeveloperRepository developerRepository;
+    private final PasswordEncoder passwordEncoder;
+    private final JwtUtil jwtUtil;
 
-    public UserService(UserRepository userRepository) {
+    @PersistenceContext
+    private EntityManager entityManager;
+
+    @Autowired
+    public UserService(UserRepository userRepository,
+                       LibraryRepository libraryRepository,
+                       DeveloperRepository developerRepository,
+                       PasswordEncoder passwordEncoder,
+                       JwtUtil jwtUtil) {
         this.userRepository = userRepository;
+        this.libraryRepository = libraryRepository;
+        this.developerRepository = developerRepository;
+        this.passwordEncoder = passwordEncoder;
+        this.jwtUtil = jwtUtil;
     }
 
     public List<User> getAllUsers() {
         return userRepository.findAll();
     }
 
+    public Optional<User> getUserById(Long id) {
+        return userRepository.findById(id);
+    }
+
     @Transactional
     public User registerUser(User user) {
-        // Set default values for a new user
+        user.setPasswordHash(passwordEncoder.encode(user.getPasswordHash()));
         user.setMemberSince(LocalDateTime.now());
         user.setAccountStatus("ACTIVE");
-        user.setWalletBalance(BigDecimal.ZERO);
-        user.setTotalSpent(BigDecimal.ZERO);
+        User savedUser = userRepository.save(user);
 
-        // Automatically create and link an empty library
         Library library = new Library();
-        library.setUser(user);
+        library.setUser(savedUser);
         library.setTotalGamesCount(0);
-        library.setTotalPlaytimeSum(0);
-        library.setAvgLibraryRating(0.0F);
+        library.setTotalPlaytimeSum(0L);
+        library.setAvgLibraryRating(0.0);
+        libraryRepository.save(library);
+
+        if ("DEVELOPER".equalsIgnoreCase(savedUser.getRole())) {
+            Developer developer = new Developer();
+            developer.setDeveloperId(savedUser.getUserId());
+            developer.setStudioName(savedUser.getFullName() + " Studio");
+            developer.setContactEmail(savedUser.getEmail());
+
+            entityManager.persist(developer);
+        }
+
+        return savedUser;
+    }
+
+    // jwt checking of user data
+    public Optional<AuthResponse> authenticateUser(String email, String rawPassword) {
+        return userRepository.findByEmail(email)
+                .filter(user -> passwordEncoder.matches(rawPassword, user.getPasswordHash()))
+                .map(user -> {
+                    String token = jwtUtil.generateToken(user.getEmail());
+                    return new AuthResponse(
+                            user.getUserId(),
+                            token,
+                            user.getFullName(),
+                            user.getRole()
+                    );
+                });
+    }
+
+    @Transactional(readOnly = true)
+    public Optional<pl.dmss.vmaneliuk.database.model.UserProfileResponse> getUserProfileById(Long id) {
+        return userRepository.findById(id)
+                .map(pl.dmss.vmaneliuk.database.model.UserProfileResponse::new);
+    }
+
+    @Transactional
+    public User addFundsToWallet(Long userId, BigDecimal amount) {
+        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("Top-up credit value must be strictly positive.");
+        }
+
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User data node reference not discovered in PostgreSQL system."));
+
+        BigDecimal currentBalance = user.getWalletBalance() != null ? user.getWalletBalance() : BigDecimal.ZERO;
+        user.setWalletBalance(currentBalance.add(amount));
 
-        user.setLibrary(library);
         return userRepository.save(user);
     }
-}
+}

+ 12 - 3
final project/database/src/main/resources/application.properties

@@ -1,15 +1,24 @@
 
 spring.application.name=database
-server.port=8084
+server.port=8085
 # Database connection URL
 spring.datasource.url=jdbc:postgresql://localhost:5432/online-game-shop
 spring.datasource.username=postgres
 spring.datasource.password=postgres
 
+spring.servlet.multipart.max-file-size=10MB
+spring.servlet.multipart.max-request-size=10MB
+logging.level.org.springframework.security=DEBUG
+logging.level.org.springframework.web=DEBUG
 # Driver class name
 spring.datasource.driver-class-name=org.postgresql.Driver
-
+spring.web.error.include-message=always
 # JPA / Hibernate properties
 spring.jpa.hibernate.ddl-auto=update
 spring.jpa.show-sql=true
-spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
+# JWT Properties
+jwt.secret=yourSuperSecretAndLongJwtKeyHere1234567890!@#
+jwt.expiration=86400000
+
+stripe.secret.key=sk_test_51TiDPoGmsm7iobSAFMc7GIWwGLRcAf5bR7Tng6SH98FEEQXO1bauUcZ7rpJ9NtJ69MY4l6PDVN7Lv0Am8hHBKVEm00cC4g0OmG

+ 9 - 0
final project/web-game-shop/src/app/models/developer.model.ts

@@ -0,0 +1,9 @@
+import { Game } from './game.model';
+
+export interface Developer {
+  developerId?: number;
+  studioName: string;
+  contactEmail: string;
+  website?: string;
+  games?: Game[];
+}

+ 14 - 0
final project/web-game-shop/src/app/models/game.model.ts

@@ -0,0 +1,14 @@
+export interface Game {
+  gameId?: number;
+  title: string;
+  description: string;
+  price: number;
+  discountPercent?: number;
+  version?: string;
+  tagsList?: string;
+  imagePath?: string;
+  purchaseDate?: string;
+  developer?: {
+    developerId: number;
+  };
+}

+ 6 - 0
final project/web-game-shop/src/app/models/library.model.ts

@@ -0,0 +1,6 @@
+export interface Library {
+  libraryId: number;
+  totalGamesCount: number;
+  totalPlaytimeSum: number;
+  avgLibraryRating: number;
+}

+ 13 - 0
final project/web-game-shop/src/app/models/user.model.ts

@@ -0,0 +1,13 @@
+ import { Library } from './library.model';
+
+export interface User {
+  userId?: number;
+  fullName: string;
+  email: string;
+  role: string;
+  passwordHash: string;
+  accountStatus?: string;
+  walletBalance?: number;
+  totalSpent?: number;
+  library?: Library;
+}

+ 25 - 0
final project/web-game-shop/src/app/styles/tailwind.config.js

@@ -0,0 +1,25 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+  content: [
+    "./src/**/*.{html,ts}",
+  ],
+  theme: {
+    extend: {
+      colors: {
+        'nexus-bg': '#0f0f16',
+        'nexus-card': '#161623',
+        'nexus-purple': '#7E5BD8',
+        'nexus-purple-hover': '#6946c1',
+        'nexus-cyan': '#00d2ff',
+        'nexus-border': 'rgba(255, 255, 255, 0.08)',
+        'nexus-text-muted': '#94a3b8',
+        'nexus-green': '#00e676'
+      },
+      fontFamily: {
+        sans: ['Inter', 'sans-serif'],
+        display: ['Rajdhani', 'sans-serif'],
+      },
+    },
+  },
+  plugins: [],
+}