diff --git a/.gitignore b/.gitignore
index d41fd5e..46db3d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,9 @@ DataAccess.iml
Presentation.iml
Application.iml
target
+DataAccessApiGateway.iml
+ApiGateway.iml
+ApplicationApiGateway.iml
### VS Code ###
.vscode/
diff --git a/ApiGateway/ApplicationApiGateway/pom.xml b/ApiGateway/ApplicationApiGateway/pom.xml
new file mode 100644
index 0000000..cc671ed
--- /dev/null
+++ b/ApiGateway/ApplicationApiGateway/pom.xml
@@ -0,0 +1,20 @@
+
+
+ 4.0.0
+
+ com.example
+ ApiGateway
+ 1.0-SNAPSHOT
+
+
+ ApplicationApiGateway
+
+
+ 21
+ 21
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java
new file mode 100644
index 0000000..8eca4f7
--- /dev/null
+++ b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Entities/User.java
@@ -0,0 +1,36 @@
+package Models.Entities;
+
+import Models.Enums.Role;
+import jakarta.persistence.*;
+import lombok.*;
+
+@Entity
+@Table(name = "users")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Getter
+ private Long id;
+
+ @Column(unique = true, nullable = false)
+ private String username;
+
+ @Column(nullable = false)
+ private String password;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ @Getter
+ private Role role;
+
+ public User(String username, String pw, Role role) {
+ this.username = username;
+ this.password = pw;
+ this.role = role;
+ }
+}
diff --git a/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java
new file mode 100644
index 0000000..e1f4b5a
--- /dev/null
+++ b/ApiGateway/ApplicationApiGateway/src/main/java/Models/Enums/Role.java
@@ -0,0 +1,6 @@
+package Models.Enums;
+
+public enum Role {
+ ADMIN,
+ CLIENT
+}
diff --git a/ApiGateway/DataAccessApiGateway/pom.xml b/ApiGateway/DataAccessApiGateway/pom.xml
new file mode 100644
index 0000000..593fa2b
--- /dev/null
+++ b/ApiGateway/DataAccessApiGateway/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+ com.example
+ ApiGateway
+ 1.0-SNAPSHOT
+
+
+ DataAccessApiGateway
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ com.example
+ ApplicationApiGateway
+ 1.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java
new file mode 100644
index 0000000..a00bcc1
--- /dev/null
+++ b/ApiGateway/DataAccessApiGateway/src/main/java/Repositories/UserRepository.java
@@ -0,0 +1,12 @@
+package Repositories;
+
+import Models.Entities.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+ Optional findByUsername(String username);
+}
diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java
new file mode 100644
index 0000000..71574be
--- /dev/null
+++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/CustomUserDetailService.java
@@ -0,0 +1,34 @@
+package Services;
+
+import Repositories.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CustomUserDetailService implements UserDetailsService {
+ private final UserRepository userRepository;
+
+ @Autowired
+ public CustomUserDetailService(UserRepository repository) {
+ this.userRepository = repository;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ var user = userRepository.findByUsername(username)
+ .orElseThrow(() -> new UsernameNotFoundException("User is not found."));
+
+ String role = "ROLE_" + user.getRole().toString();
+
+ return org.springframework.security.core.userdetails.User
+ .withUsername(user.getUsername())
+ .password(user.getPassword())
+ .authorities(new SimpleGrantedAuthority(role))
+ .build();
+ }
+
+}
diff --git a/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java
new file mode 100644
index 0000000..f4a9955
--- /dev/null
+++ b/ApiGateway/DataAccessApiGateway/src/main/java/Services/UserService.java
@@ -0,0 +1,31 @@
+package Services;
+
+import Models.Entities.User;
+import Models.Enums.Role;
+import Repositories.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class UserService {
+ private final UserRepository userRepository;
+ private final PasswordEncoder encoder;
+
+ @Autowired
+ public UserService(UserRepository repository, PasswordEncoder passwordEncoder) {
+ this.userRepository = repository;
+ this.encoder = passwordEncoder;
+ }
+
+ public User CreateUser(String username, String password, Role role) {
+ System.out.println("Creating user: " + username + " with role: " + role);
+ String pwHash = encoder.encode(password);
+ User user = new User(username, pwHash, role);
+ return userRepository.save(user);
+ }
+
+ public User FindUserByUsername(String username) {
+ return userRepository.findByUsername(username).orElse(null);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/pom.xml b/ApiGateway/PresentationApiGateway/pom.xml
new file mode 100644
index 0000000..0276eb5
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/pom.xml
@@ -0,0 +1,54 @@
+
+
+ 4.0.0
+
+ com.example
+ ApiGateway
+ 1.0-SNAPSHOT
+
+
+ PresentationApiGateway
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+
+
+ com.example
+ DataAccessApiGateway
+ 1.0-SNAPSHOT
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+ Console.ApiGatewayApp
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java
new file mode 100644
index 0000000..802a70f
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/RestTemplateConfig.java
@@ -0,0 +1,13 @@
+package Configs;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+ @Bean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java
new file mode 100644
index 0000000..77aafab
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Configs/SecurityConfig.java
@@ -0,0 +1,66 @@
+package Configs;
+
+import JWT.JwtAuthFilter;
+import Services.CustomUserDetailService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+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 java.util.List;
+
+@Configuration
+@EnableMethodSecurity
+public class SecurityConfig {
+
+ private final JwtAuthFilter jwtAuthFilter;
+ private final CustomUserDetailService userDetailService;
+
+ public SecurityConfig(JwtAuthFilter jwtAuthFilter, CustomUserDetailService userDetailService) {
+ this.jwtAuthFilter = jwtAuthFilter;
+ this.userDetailService = userDetailService;
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(AbstractHttpConfigurer::disable)
+ .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers("/login").permitAll()
+ .requestMatchers("/admin/**").hasAuthority("ROLE_ADMIN")
+ .requestMatchers("/client/**").hasAuthority("ROLE_CLIENT")
+ .anyRequest().authenticated()
+ )
+ .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); // Добавляем фильтр JWT
+
+ return http.build();
+ }
+
+ @Bean
+ public DaoAuthenticationProvider daoAuthenticationProvider() {
+ DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
+ provider.setUserDetailsService(userDetailService);
+ provider.setPasswordEncoder(passwordEncoder());
+ return provider;
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(DaoAuthenticationProvider daoAuthProvider) {
+ return new ProviderManager(List.of(daoAuthenticationProvider()));
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java b/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java
new file mode 100644
index 0000000..05d88fb
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Console/ApiGatewayApp.java
@@ -0,0 +1,23 @@
+package Console;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+
+@SpringBootApplication(scanBasePackages = {
+ "Controllers",
+ "Configs",
+ "JWT",
+ "Services",
+ "Repositories",
+ "Models.Entities",
+ "Models.Enums"
+})
+@EnableJpaRepositories(basePackages = "Repositories")
+@EntityScan(basePackages = "Models.Entities")
+public class ApiGatewayApp {
+ public static void main(String[] args) {
+ SpringApplication.run(ApiGatewayApp.class, args);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java b/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java
new file mode 100644
index 0000000..cf26fa1
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Console/PasswordEncoderTest.java
@@ -0,0 +1,12 @@
+package Console;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class PasswordEncoderTest {
+ public static void main(String[] args) {
+ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+ String password = "admin123";
+ String encodedPassword = encoder.encode(password);
+ System.out.println(encodedPassword);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java
new file mode 100644
index 0000000..ab1931f
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AdminController.java
@@ -0,0 +1,89 @@
+package Controllers;
+
+import DTO.BankAccountDTO;
+import DTO.OperationDTO;
+import DTO.UserDTO;
+import Requests.UserFilterRequest;
+import Requests.UserRequest;
+import Services.AdminService;
+import Services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin")
+public class AdminController {
+ private final UserService userService;
+ private final AdminService adminService;
+
+ @Autowired
+ public AdminController(UserService userService, AdminService adminService) {
+ this.userService = userService;
+ this.adminService = adminService;
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @PostMapping("/users/create")
+ public ResponseEntity CreateUser(@RequestBody UserRequest userRequest) {
+ userService.CreateUser(userRequest.getUsername(), userRequest.getPassword(), userRequest.getRole());
+ return ResponseEntity.ok("User created successfully.");
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/users")
+ public ResponseEntity> getAllUsersFiltered(
+ @RequestParam(required = false) String sex,
+ @RequestParam(required = false) String hairColor
+ ) {
+ UserFilterRequest request = new UserFilterRequest();
+ request.setSex(sex);
+ request.setHairColor(hairColor);
+ return ResponseEntity.ok(adminService.getFilteredUsers(request));
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/users/{id}")
+ public ResponseEntity getUserById(@PathVariable int id) {
+ return ResponseEntity.ok(adminService.getUserById(id));
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/bankaccounts")
+ public ResponseEntity> getAllBankAccounts() {
+ return ResponseEntity.ok(adminService.getAllBankAccounts());
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/bankaccounts/user/{userId}")
+ public ResponseEntity> getAccountsByUserId(@PathVariable int userId) {
+ return ResponseEntity.ok(adminService.getAccountsByUserId(userId));
+ }
+
+ @PreAuthorize("hasRole('ADMIN')")
+ @GetMapping("/bankaccounts/{id}/operations")
+ public ResponseEntity> getOperationsByAccountId(
+ @PathVariable int id,
+ @RequestParam(required = false) String type
+ ) {
+ return ResponseEntity.ok(adminService.getOperationsByAccountId(id, type));
+ }
+
+
+ @PreAuthorize("isAuthenticated()")
+ @PostMapping("/logout")
+ public ResponseEntity> Logout() {
+ try {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ return ResponseEntity.ok("Admin " + authentication.getName() + " logged out.");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return ResponseEntity.status(500).body("Error during logout: " + e.getMessage());
+ }
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java
new file mode 100644
index 0000000..1da9b2c
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/AuthController.java
@@ -0,0 +1,58 @@
+package Controllers;
+
+import JWT.JwtUtil;
+import Requests.LoginRequest;
+import Responses.JwtResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+public class AuthController {
+
+ private final AuthenticationManager authenticationManager;
+ private final JwtUtil jwtUtil;
+ private final UserDetailsService userDetailsService;
+
+ @Autowired
+ public AuthController(AuthenticationManager authenticationManager, JwtUtil jwtUtil, UserDetailsService userDetailsService) {
+ this.authenticationManager = authenticationManager;
+ this.jwtUtil = jwtUtil;
+ this.userDetailsService = userDetailsService;
+ }
+
+ @GetMapping("/login")
+ public ResponseEntity getLoginInfo() {
+ return ResponseEntity.status(HttpStatus.OK).body("Please send a POST request to /login with your credentials.");
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity login(@RequestBody LoginRequest loginRequest) {
+ try {
+ if (loginRequest.getUsername() == null || loginRequest.getPassword() == null) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JwtResponse("Username and password are required"));
+ }
+
+ Authentication auth = new UsernamePasswordAuthenticationToken(
+ loginRequest.getUsername(), loginRequest.getPassword()
+ );
+
+ authenticationManager.authenticate(auth);
+
+ UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());
+ String jwt = jwtUtil.generateToken(userDetails);
+ return ResponseEntity.ok(new JwtResponse(jwt));
+ } catch (BadCredentialsException e) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new JwtResponse("Invalid username or password"));
+ } catch (Exception e) {
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new JwtResponse("An unexpected error occurred"));
+ }
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java
new file mode 100644
index 0000000..d2f5893
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/ClientController.java
@@ -0,0 +1,80 @@
+package Controllers;
+
+import Services.ClientService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/client")
+public class ClientController {
+
+ private final ClientService clientService;
+
+ @Autowired
+ public ClientController(ClientService clientService) {
+ this.clientService = clientService;
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @GetMapping("/me")
+ public ResponseEntity> getCurrentUser() {
+ return clientService.getCurrentUser();
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @GetMapping("/accounts")
+ public ResponseEntity> getMyAccounts() {
+ return clientService.getMyAccounts();
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @GetMapping("/accounts/{id}")
+ public ResponseEntity> getAccountById(@PathVariable int id) {
+ return clientService.getAccountById(id);
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @PostMapping("/friends/add/{otherId}")
+ public ResponseEntity> addFriend(@PathVariable int otherId) {
+ return clientService.addFriend(otherId);
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @PostMapping("/friends/remove/{otherId}")
+ public ResponseEntity> removeFriend(@PathVariable int otherId) {
+ return clientService.removeFriend(otherId);
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @PostMapping("/accounts/{accountId}/deposit/{amount}")
+ public ResponseEntity> deposit(@PathVariable int accountId, @PathVariable double amount) {
+ return clientService.deposit(accountId, amount);
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @PostMapping("/accounts/{accountId}/withdraw/{amount}")
+ public ResponseEntity> withdraw(@PathVariable int accountId, @PathVariable double amount) {
+ return clientService.withdraw(accountId, amount);
+ }
+
+ @PreAuthorize("hasRole('CLIENT')")
+ @PostMapping("/accounts/transfer/{fromId}/{toId}/{amount}")
+ public ResponseEntity> transfer(
+ @PathVariable int fromId,
+ @PathVariable int toId,
+ @PathVariable double amount
+ ) {
+ return clientService.transfer(fromId, toId, amount);
+ }
+
+ @PreAuthorize("isAuthenticated()")
+ @PostMapping("/logout")
+ public ResponseEntity> logout() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ return ResponseEntity.ok("Client " + authentication.getName() + " logged out.");
+ }
+}
\ No newline at end of file
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java
new file mode 100644
index 0000000..c18c0f4
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Controllers/Handlers/GlobalExceptionHandler.java
@@ -0,0 +1,18 @@
+package Controllers.Handlers;
+
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity> handleException(Exception e) {
+ e.printStackTrace();
+ return ResponseEntity
+ .status(HttpStatus.INTERNAL_SERVER_ERROR)
+ .body("Error: " + e.getMessage());
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java
new file mode 100644
index 0000000..c795c80
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/BankAccountDTO.java
@@ -0,0 +1,15 @@
+package DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class BankAccountDTO {
+ private Integer id;
+ private Double balance;
+ private String userLogin;
+ private Integer userId;
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java
new file mode 100644
index 0000000..f9c1e1e
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/OperationDTO.java
@@ -0,0 +1,15 @@
+package DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class OperationDTO {
+ private Integer id;
+ private String type;
+ private Double amount;
+ private Integer accountId;
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java b/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java
new file mode 100644
index 0000000..fbe6357
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/DTO/UserDTO.java
@@ -0,0 +1,21 @@
+package DTO;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class UserDTO {
+ private Integer id;
+ private String login;
+ private String name;
+ private Integer age;
+ private String sex;
+ private String hairType;
+ private List friendIds;
+ private List bankAccountIds;
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java
new file mode 100644
index 0000000..65f829c
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpAdminUtil.java
@@ -0,0 +1,26 @@
+package JWT;
+
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HttpAdminUtil {
+ private String getCurrentToken() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication.getCredentials() instanceof String token) {
+ return token;
+ }
+ return null;
+ }
+
+ public HttpEntity> withAuthHeaders() {
+ String token = getCurrentToken();
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Authorization", "Bearer " + token);
+ return new HttpEntity<>(headers);
+ }
+}
+
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java
new file mode 100644
index 0000000..1fc2292
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/HttpClientUtil.java
@@ -0,0 +1,32 @@
+package JWT;
+
+import Services.UserService;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HttpClientUtil {
+ private String getCurrentToken() {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication.getCredentials() instanceof String token) {
+ return token;
+ }
+ return null;
+ }
+
+ public Long getCurrentUserId(UserService userService) {
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ String username = authentication.getName();
+ return userService.FindUserByUsername(username).getId();
+ }
+
+ public HttpEntity> withAuthHeaders() {
+ String token = getCurrentToken();
+ HttpHeaders headers = new HttpHeaders();
+ headers.set("Authorization", "Bearer " + token);
+ return new HttpEntity<>(headers);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java
new file mode 100644
index 0000000..28bf617
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtAuthFilter.java
@@ -0,0 +1,59 @@
+package JWT;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class JwtAuthFilter extends OncePerRequestFilter {
+
+ private final JwtUtil jwtUtil;
+ private final UserDetailsService userDetailsService;
+
+ @Autowired
+ public JwtAuthFilter(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
+ this.jwtUtil = jwtUtil;
+ this.userDetailsService = userDetailsService;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ String path = request.getServletPath();
+ if (path.startsWith("/auth") || path.equals("/login")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ String authHeader = request.getHeader("Authorization");
+ String username = null;
+ String jwt = null;
+
+ if (authHeader != null && authHeader.startsWith("Bearer ")) {
+ jwt = authHeader.substring(7);
+ username = jwtUtil.extractUsername(jwt);
+ }
+
+ if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+ UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
+ if (jwtUtil.validateToken(jwt, userDetails)) {
+ UsernamePasswordAuthenticationToken authToken =
+ new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java
new file mode 100644
index 0000000..dcc0c2b
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/JWT/JwtUtil.java
@@ -0,0 +1,82 @@
+package JWT;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.security.Keys;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Component
+public class JwtUtil {
+
+ @Value("${jwt.secret}")
+ private String secret;
+
+ @Value("${jwt.expiration}")
+ private Long expiration;
+
+ private SecretKey getSecretKey() {
+ return Keys.hmacShaKeyFor(secret.getBytes());
+ }
+
+ public String extractUsername(String token) {
+ return extractClaim(token, Claims::getSubject);
+ }
+
+ public Date extractExpiration(String token) {
+ return extractClaim(token, Claims::getExpiration);
+ }
+
+ public T extractClaim(String token, Function claimsResolver) {
+ final Claims claims = extractAllClaims(token);
+ return claimsResolver.apply(claims);
+ }
+
+ private Claims extractAllClaims(String token){
+ return Jwts
+ .parser()
+ .verifyWith(getSecretKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload();
+ }
+
+ private Boolean isTokenExpired(String token) {
+ return extractExpiration(token).before(new Date());
+ }
+
+ public String generateToken(UserDetails userDetails) {
+ Map claims = new HashMap<>();
+ claims.put("role", userDetails.getAuthorities().stream()
+ .map(grantedAuthority -> grantedAuthority.getAuthority())
+ .collect(Collectors.toList()));
+
+ return createToken(claims, userDetails.getUsername());
+ }
+
+ private String createToken(Map claims, String subject) {
+ return Jwts.builder()
+ .setClaims(claims)
+ .setSubject(subject)
+ .setIssuedAt(new Date(System.currentTimeMillis()))
+ .setExpiration(new Date(System.currentTimeMillis() + expiration))
+ .signWith(getSecretKey(), SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+ public Boolean validateToken(String token, UserDetails userDetails) {
+ final String username = extractUsername(token);
+ return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
+ }
+
+ public List extractRoles(String token) {
+ return extractClaim(token, claims -> claims.get("role", List.class));
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java
new file mode 100644
index 0000000..2c4651b
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/LoginRequest.java
@@ -0,0 +1,24 @@
+package Requests;
+
+public class LoginRequest {
+ private String username;
+ private String password;
+
+ public LoginRequest() {}
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java
new file mode 100644
index 0000000..4b47a63
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserFilterRequest.java
@@ -0,0 +1,9 @@
+package Requests;
+
+import lombok.Data;
+
+@Data
+public class UserFilterRequest {
+ private String sex;
+ private String hairColor;
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java
new file mode 100644
index 0000000..db2994f
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Requests/UserRequest.java
@@ -0,0 +1,13 @@
+package Requests;
+
+import Models.Enums.Role;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+public class UserRequest {
+ private String username;
+ private String password;
+ private Role role;
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java b/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java
new file mode 100644
index 0000000..3d5abad
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Responses/JwtResponse.java
@@ -0,0 +1,17 @@
+package Responses;
+
+public class JwtResponse {
+ private String token;
+
+ public JwtResponse(String token) {
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java b/ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java
new file mode 100644
index 0000000..721f566
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Services/AdminService.java
@@ -0,0 +1,91 @@
+package Services;
+
+import DTO.BankAccountDTO;
+import DTO.OperationDTO;
+import DTO.UserDTO;
+import JWT.HttpAdminUtil;
+import Requests.UserFilterRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Arrays;
+import java.util.List;
+
+@Service
+public class AdminService {
+
+ private final RestTemplate restTemplate;
+ private final HttpAdminUtil adminUtil;
+
+ @Autowired
+ public AdminService(RestTemplate restTemplate, HttpAdminUtil adminUtil) {
+ this.restTemplate = restTemplate;
+ this.adminUtil = adminUtil;
+ }
+
+ public List getFilteredUsers(UserFilterRequest filterRequest) {
+ String url = "http://localhost:8080/data/users?sex=" +
+ (filterRequest.getSex() != null ? filterRequest.getSex() : "") +
+ "&hairColor=" + (filterRequest.getHairColor() != null ? filterRequest.getHairColor() : "");
+
+ ResponseEntity> response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ adminUtil.withAuthHeaders(),
+ new ParameterizedTypeReference<>() {}
+ );
+ return response.getBody();
+ }
+
+ public UserDTO getUserById(int id) {
+ String url = "http://localhost:8080/users/" + id;
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ adminUtil.withAuthHeaders(),
+ UserDTO.class
+ );
+ return response.getBody();
+ }
+
+ public List getAllBankAccounts() {
+ String url = "http://localhost:8080/data/accounts";
+ ResponseEntity> response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ adminUtil.withAuthHeaders(),
+ new ParameterizedTypeReference<>() {}
+ );
+ return response.getBody();
+ }
+
+ public List getAccountsByUserId(int userId) {
+ String url = "http://localhost:8080/data/users/" + userId + "/accounts";
+ ResponseEntity response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ adminUtil.withAuthHeaders(),
+ BankAccountDTO[].class
+ );
+ return Arrays.asList(response.getBody());
+ }
+
+ public List getOperationsByAccountId(int accountId, String type) {
+ String url = "http://localhost:8080/data/operations?accountId=" + accountId;
+ if (type != null && !type.isEmpty()) {
+ url += "&type=" + type;
+ }
+
+ ResponseEntity> response = restTemplate.exchange(
+ url,
+ HttpMethod.GET,
+ adminUtil.withAuthHeaders(),
+ new ParameterizedTypeReference<>() {}
+ );
+ return response.getBody();
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/java/Services/ClientService.java b/ApiGateway/PresentationApiGateway/src/main/java/Services/ClientService.java
new file mode 100644
index 0000000..2872e98
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/java/Services/ClientService.java
@@ -0,0 +1,89 @@
+package Services;
+
+import DTO.BankAccountDTO;
+import DTO.UserDTO;
+import JWT.HttpClientUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Objects;
+
+@Service
+public class ClientService {
+
+ private final RestTemplate restTemplate;
+ private final HttpClientUtil clientUtil;
+ private final UserService userService;
+
+ @Autowired
+ public ClientService(RestTemplate restTemplate, HttpClientUtil clientUtil, UserService userService) {
+ this.restTemplate = restTemplate;
+ this.clientUtil = clientUtil;
+ this.userService = userService;
+ }
+
+ public ResponseEntity getCurrentUser() {
+ Long userId = clientUtil.getCurrentUserId(userService);
+ String url = "http://localhost:8080/users/" + userId;
+ return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), UserDTO.class);
+ }
+
+ public ResponseEntity getMyAccounts() {
+ Long userId = clientUtil.getCurrentUserId(userService);
+ String url = "http://localhost:8080/data/users/" + userId + "/accounts";
+ return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class);
+ }
+
+ public ResponseEntity> getAccountById(int id) {
+ Long userId = clientUtil.getCurrentUserId(userService);
+ String userAccountsUrl = "http://localhost:8080/data/users/" + userId + "/accounts";
+ ResponseEntity accountsResponse = restTemplate.exchange(
+ userAccountsUrl, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO[].class);
+
+ boolean ownsAccount = false;
+ if (accountsResponse.getStatusCode().is2xxSuccessful()) {
+ for (BankAccountDTO account : Objects.requireNonNull(accountsResponse.getBody())) {
+ if (account.getId() == id) {
+ ownsAccount = true;
+ break;
+ }
+ }
+ }
+
+ if (!ownsAccount) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied to account.");
+ }
+
+ String url = "http://localhost:8080/data/accounts/" + id;
+ return restTemplate.exchange(url, HttpMethod.GET, clientUtil.withAuthHeaders(), BankAccountDTO.class);
+ }
+
+ public ResponseEntity addFriend(int otherId) {
+ Long userId = clientUtil.getCurrentUserId(userService);
+ String url = "http://localhost:8080/interactions/friends/add/" + userId + "/" + otherId;
+ return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class);
+ }
+
+ public ResponseEntity removeFriend(int otherId) {
+ Long userId = clientUtil.getCurrentUserId(userService);
+ String url = "http://localhost:8080/interactions/friends/remove/" + userId + "/" + otherId;
+ return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class);
+ }
+
+ public ResponseEntity deposit(int accountId, double amount) {
+ String url = "http://localhost:8080/interactions/deposit/" + accountId + "/" + amount;
+ return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class);
+ }
+
+ public ResponseEntity withdraw(int accountId, double amount) {
+ String url = "http://localhost:8080/interactions/withdraw/" + accountId + "/" + amount;
+ return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class);
+ }
+
+ public ResponseEntity transfer(int fromId, int toId, double amount) {
+ String url = "http://localhost:8080/interactions/transfer/" + fromId + "/" + toId + "/" + amount;
+ return restTemplate.exchange(url, HttpMethod.POST, clientUtil.withAuthHeaders(), String.class);
+ }
+}
diff --git a/ApiGateway/PresentationApiGateway/src/main/resources/application.yml b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml
new file mode 100644
index 0000000..22e8c38
--- /dev/null
+++ b/ApiGateway/PresentationApiGateway/src/main/resources/application.yml
@@ -0,0 +1,40 @@
+spring:
+ application:
+ name: ApiGateway
+
+ main:
+ allow-bean-definition-overriding: true
+
+ flyway:
+ enabled: false
+
+ datasource:
+ url: jdbc:postgresql://localhost:5432/JavaLabsApiDb
+ username: postgres
+ password: limosha
+ driver-class-name: org.postgresql.Driver
+
+ jpa:
+ hibernate:
+ ddl-auto: validate
+ show-sql: true
+ open-in-view: false
+ properties:
+ hibernate:
+ format_sql: true
+
+ thymeleaf:
+ enabled: false
+ check-template-location: false
+
+# JWT Configuration
+jwt:
+ secret: Z2FsdGZpZDpxNDqf9r1mPZYuZAsZZEwOovUEY9NhcY46uo3bB9XzRXMKbK6R03Qp
+ expiration: 86400000 # 1 day
+
+server:
+ port: 8081
+ error:
+ include-message: always
+ include-binding-errors: always
+ include-stacktrace: never
diff --git a/ApiGateway/pom.xml b/ApiGateway/pom.xml
new file mode 100644
index 0000000..b64166c
--- /dev/null
+++ b/ApiGateway/pom.xml
@@ -0,0 +1,90 @@
+
+
+ 4.0.0
+
+ com.example
+ lim0sha
+ 1.0-SNAPSHOT
+
+
+ ApiGateway
+ pom
+
+ ApplicationApiGateway
+ PresentationApiGateway
+
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+
+ org.thymeleaf.extras
+ thymeleaf-extras-springsecurity6
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.3
+ runtime
+
+
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.3
+ runtime
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.3
+
+
+
+
+ io.github.cdimascio
+ dotenv-java
+ 2.2.4
+
+
+
+
+
\ No newline at end of file
diff --git a/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java b/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java
index b008660..0faeafc 100644
--- a/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java
+++ b/Presentation/src/main/java/Presentation/Controllers/UserDTOController.java
@@ -45,7 +45,7 @@ public ResponseEntity getUserById(@Parameter(description = "ID поль
@ApiResponse(responseCode = "201", description = "Пользователь создан"),
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
- @PostMapping
+ @PostMapping("/create")
public ResponseEntity> createUser(@RequestBody User user) {
UserResult result = baseController.CreateUser(user);
if (result instanceof UserResult.Success) {
@@ -61,7 +61,7 @@ public ResponseEntity> createUser(@RequestBody User user) {
@ApiResponse(responseCode = "404", description = "Пользователь не найден"),
@ApiResponse(responseCode = "400", description = "Некорректные данные")
})
- @PutMapping("/{id}")
+ @PutMapping("/update/{id}")
public ResponseEntity> updateUser(@Parameter(description = "ID пользователя") @PathVariable int id, @RequestBody User user) {
User existingUser = baseController.GetUserById(id);
if (existingUser == null) {
@@ -80,7 +80,7 @@ public ResponseEntity> updateUser(@Parameter(description = "ID пользов
@ApiResponse(responseCode = "204", description = "Пользователь удален"),
@ApiResponse(responseCode = "404", description = "Пользователь не найден")
})
- @DeleteMapping("/{id}")
+ @DeleteMapping("/delete/{id}")
public ResponseEntity> deleteUser(@Parameter(description = "ID пользователя") @PathVariable int id) {
User user = baseController.GetUserById(id);
if (user == null) {
diff --git a/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java b/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java
new file mode 100644
index 0000000..53fe9be
--- /dev/null
+++ b/Presentation/src/main/java/Presentation/Controllers/UserInteractionsController.java
@@ -0,0 +1,111 @@
+package Presentation.Controllers;
+
+import Application.Models.Entities.BankAccount;
+import Application.ResultTypes.OperationResult;
+import Presentation.Interfaces.IBaseController;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/interactions")
+public class UserInteractionsController {
+
+ private final IBaseController baseController;
+
+ @Autowired
+ public UserInteractionsController(IBaseController baseController) {
+ this.baseController = baseController;
+ }
+
+ @Operation(summary = "Добавить друга", description = "Добавляет пользователя в друзья")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Друг добавлен"),
+ @ApiResponse(responseCode = "400", description = "Ошибка добавления")
+ })
+ @PostMapping("/friends/add/{userId}/{otherId}")
+ public ResponseEntity addFriend(
+ @PathVariable int userId,
+ @PathVariable int otherId
+ ) {
+ baseController.AddFriend(userId, otherId);
+ return ResponseEntity.ok("Friend added.");
+ }
+
+ @Operation(summary = "Удалить друга", description = "Удаляет пользователя из друзей")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Друг удалён"),
+ @ApiResponse(responseCode = "400", description = "Ошибка удаления")
+ })
+ @PostMapping("/friends/remove/{userId}/{otherId}")
+ public ResponseEntity removeFriend(
+ @PathVariable int userId,
+ @PathVariable int otherId
+ ) {
+ baseController.RemoveFriend(userId, otherId);
+ return ResponseEntity.ok("Friend removed.");
+ }
+
+ @Operation(summary = "Пополнить счёт", description = "Осуществляет пополнение счёта")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Пополнение выполнено"),
+ @ApiResponse(responseCode = "400", description = "Ошибка пополнения")
+ })
+ @PostMapping("/deposit/{accountId}/{amount}")
+ public ResponseEntity deposit(
+ @PathVariable int accountId,
+ @PathVariable double amount
+ ) {
+ BankAccount account = baseController.GetBankAccountById(accountId);
+ OperationResult result = baseController.Deposit(account, amount);
+
+ if (result instanceof OperationResult.Success) {
+ return ResponseEntity.ok("Deposit successful.");
+ }
+ return ResponseEntity.badRequest().body(result.toString());
+ }
+
+ @Operation(summary = "Снять со счёта", description = "Осуществляет снятие средств со счёта")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Снятие выполнено"),
+ @ApiResponse(responseCode = "400", description = "Ошибка снятия")
+ })
+ @PostMapping("/withdraw/{accountId}/{amount}")
+ public ResponseEntity withdraw(
+ @PathVariable int accountId,
+ @PathVariable double amount
+ ) {
+ BankAccount account = baseController.GetBankAccountById(accountId);
+ OperationResult result = baseController.Withdraw(account, amount);
+
+ if (result instanceof OperationResult.Success) {
+ return ResponseEntity.ok("Withdrawal successful.");
+ }
+ return ResponseEntity.badRequest().body(result.toString());
+ }
+
+ @Operation(summary = "Перевод между счетами", description = "Выполняет перевод между счетами с учётом комиссии")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "Перевод выполнен"),
+ @ApiResponse(responseCode = "400", description = "Ошибка перевода")
+ })
+ @PostMapping("/transfer/{fromAccountId}/{toAccountId}/{amount}")
+ public ResponseEntity transfer(
+ @PathVariable int fromAccountId,
+ @PathVariable int toAccountId,
+ @PathVariable double amount
+ ) {
+ BankAccount fromAccount = baseController.GetBankAccountById(fromAccountId);
+ BankAccount toAccount = baseController.GetBankAccountById(toAccountId);
+
+ OperationResult result = baseController.Transfer(fromAccount, toAccount, amount);
+
+ if (result instanceof OperationResult.Success) {
+ return ResponseEntity.ok("Transfer successful.");
+ }
+ return ResponseEntity.badRequest().body(result.toString());
+ }
+}
diff --git a/pom.xml b/pom.xml
index 3b6964c..b7f3d90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,6 +96,8 @@
Application
DataAccess
Presentation
+ ApiGateway
+ ApiGateway/DataAccessApiGateway