diff --git a/.env b/.env index a77b2016545f93b50c6658c242bc2f46f7b29ac5..8244fc7930d8fa61cd61b5ba28a0d021fb14dc27 100644 --- a/.env +++ b/.env @@ -2,31 +2,39 @@ # Environment ####################################### -## Meter aquà cosas sobre las keys del token y esas mierdas - ####################################### # APIs ####################################### ## AUTH -API_AUTH_ENDPOINT=/users API_AUTH_HOSTNAME=auth-api API_AUTH_PORT=8101 -API_AUTH_URL=http://$API_AUTH_HOSTNAME:$API_AUTH_PORT$API_AUTH_ENDPOINT +API_AUTH_URL=http://$API_AUTH_HOSTNAME:$API_AUTH_PORT +API_AUTH_TOKEN_URL=$API_AUTH_URL/token ## Bookings -API_BOOKINGS_ENDPOINT=/bookings API_BOOKINGS_HOSTNAME=bookings-api API_BOOKINGS_PORT=8401 -API_BOOKINGS_URL=http://$API_BOOKINGS_HOSTNAME:$API_BOOKINGS_PORT$API_BOOKINGS_ENDPOINT +API_BOOKINGS_URL=http://$API_BOOKINGS_HOSTNAME:$API_BOOKINGS_PORT ## Hotels -API_HOTELS_ENDPOINT=/hotels API_HOTELS_HOSTNAME=hotels-api API_HOTELS_PORT=8301 -API_HOTELS_URL=http://$API_HOTELS_HOSTNAME:$API_HOTELS_PORT$API_HOTELS_ENDPOINT +API_HOTELS_URL=http://$API_HOTELS_HOSTNAME:$API_HOTELS_PORT ## Users -API_USERS_ENDPOINT=/users API_USERS_HOSTNAME=users-api API_USERS_PORT=8201 -API_USERS_URL=http://$API_USERS_HOSTNAME:$API_USERS_PORT$API_USERS_ENDPOINT +API_USERS_URL=http://$API_USERS_HOSTNAME:$API_USERS_PORT +## Kong +# Kong configuration +API_KONG_HOSTNAME=kong +API_KONG_PORT=8000 +API_KONG_URL=http://$API_KONG_HOSTNAME:$API_KONG_PORT +API_KONG_GATEWAY=$API_KONG_URL/api +# Kong Routes +API_KONG_ROUTE_AUTH_URL=$API_KONG_GATEWAY/auth +API_KONG_ROUTE_BOOKINGS_URL=$API_KONG_GATEWAY/bookings +API_KONG_ROUTE_CLIENTS_URL=$API_KONG_GATEWAY/users/clients +API_KONG_ROUTE_HOTELS_URL=$API_KONG_GATEWAY/hotels +API_KONG_ROUTE_MANAGERS_URL=$API_KONG_GATEWAY/users/managers +API_KONG_ROUTE_USERS_URL=$API_KONG_GATEWAY/users # ####################################### # Databases @@ -40,3 +48,9 @@ DB_HOTELS_HOSTNAME=HotelsDB ## DB Users DB_USERS_DATABASE=Users DB_USERS_HOSTNAME=UsersDB +# +####################################### +# JWT +####################################### +JWT_KEY=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot +JWT_KID=9L8RmLEKMFd54WrR06CE9zoRSAGpCWeu diff --git a/.gitignore b/.gitignore index 9d69baa07a9349a0ed0d73d664ad697aa5e65a9f..734c0ce60a65212ae939d5600cc49e6a47cc821a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,9 @@ ### Own ### -taller +.trunk/ *.pdf **/target/ **/.vscode **/tmp **/*.tmp *.ln -*.log - +*.log \ No newline at end of file diff --git a/diagramas/practica.asta b/diagramas/practica.asta new file mode 100644 index 0000000000000000000000000000000000000000..78f91509c528f8268b56577e3bdff74384871e02 Binary files /dev/null and b/diagramas/practica.asta differ diff --git a/docker-compose.yml b/docker-compose.yml index 57fc680a4356510f5cb4d24b3d76d106377c2bf8..2244d32ca089bb97babffde32e46f734637d88cd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,26 @@ networks: #Red interna para comunicar los servicios kong-net: services: + ####################################### + # Cliente Angular + ####################################### + # RoomsBooking-Web: + # image: roomsbooking-web-image + # build: + # context: ./angular/RestClient + # dockerfile: ./Dockerfile + # restart: unless-stopped + # ports: + # - 4200:80 + # networks: + # - kong-net + # depends_on: + # - kong + # - Auth-API + # - Users-API + # - Hotels-API + # - Bookings-API + ####################################### # Kong ####################################### @@ -15,7 +35,7 @@ services: # Postgres: The database used by Kong kong-database: image: postgres:9.6 - container_DATABASE: kong-postgres + container_name: kong-postgres restart: on-failure networks: - kong-net @@ -108,7 +128,26 @@ services: kong-database: condition: service_healthy ports: - - 1337:133 + - 1337:1337 + + ####################################### + # Generate Data + ####################################### + + Generate-APIs-Data: + image: gen-apis-data + build: + context: ./poblate + dockerfile: Dockerfile + networks: + - kong-net + restart: on-failure + depends_on: + - kong + - Auth-API + - Users-API + - Hotels-API + - Bookings-API ####################################### # APIs @@ -124,10 +163,11 @@ services: networks: - kong-net environment: - # TODO Meter kid y secret key - KID: ${JWT_KID} - SECRET_KEY: ${JWT_KID} - EXTERNAL_SERVICE_USERS_URL: ${API_USERS_URL}${API_USERS_ENDPOINT} + SECURITY_JWT_SECRET-KEY: ${JWT_KEY} + SECURITY_JWT_KID: ${JWT_KID} + SECURITY_JWT_EXTERNAL_EXPIRATION: ${JWT_EXTERNAL_EXPIRATION:-3600} + SECURITY_JWT_INTERNAL_EXPIRATION: ${JWT_INTERNAL_EXPIRATION:-600} + SERVICES_EXTERNAL_USERS_URL: ${API_KONG_ROUTE_USERS_URL} depends_on: - Users-API @@ -135,14 +175,16 @@ services: image: users-api-image hostname: ${API_USERS_HOSTNAME} build: - context: ./java/services/user + context: ./java/services/users dockerfile: Dockerfile restart: unless-stopped networks: - kong-net environment: SPRING_DATASOURCE_URL: jdbc:mysql://${DB_USERS_HOSTNAME}:${DB_USERS_PORT:-3306}/${DB_USERS_DATABASE}?createDatabaseIfNotExist=true - EXTERNAL_SERVICE_USERS_URL: ${API_USERS_URL}${API_USERS_ENDPOINT} + SERVICES_INTERNAL_TOKEN_URL: ${API_AUTH_TOKEN_URL} + SERVICES_EXTERNAL_HOTELS_URL: ${API_KONG_ROUTE_HOTELS_URL} + SERVICES_EXTERNAL_BOOKINGS_URL: ${API_KONG_ROUTE_BOOKINGS_URL} depends_on: - Users-DB @@ -157,6 +199,9 @@ services: - kong-net environment: SPRING_DATASOURCE_URL: jdbc:mysql://${DB_HOTELS_HOSTNAME}:${DB_HOTELS_PORT:-3306}/${DB_HOTELS_DATABASE}?createDatabaseIfNotExist=true + SERVICES_INTERNAL_TOKEN_URL: ${API_AUTH_TOKEN_URL} + SERVICES_EXTERNAL_MANAGERS_URL: ${API_KONG_ROUTE_MANAGERS_URL} + SERVICES_EXTERNAL_BOOKINGS_URL: ${API_KONG_ROUTE_BOOKINGS_URL} depends_on: - Hotels-DB @@ -171,6 +216,11 @@ services: - kong-net environment: SPRING_DATASOURCE_URL: jdbc:mysql://${DB_BOOKINGS_HOSTNAME}:${DB_BOOKINGS_PORT:-3306}/${DB_BOOKINGS_DATABASE}?createDatabaseIfNotExist=true + SERVICES_INTERNAL_TOKEN_URL: ${API_AUTH_TOKEN_URL} + SERVICES_EXTERNAL_CLIENTS_URL: ${API_KONG_ROUTE_CLIENTS_URL} + SERVICES_EXTERNAL_MANAGERS_URL: ${API_KONG_ROUTE_MANAGERS_URL} + SERVICES_EXTERNAL_HOTELS_URL: ${API_KONG_ROUTE_HOTELS_URL} + depends_on: - Users-API - Hotels-API @@ -223,26 +273,10 @@ services: networks: - kong-net volumes: - - hotels_data:/var/lib/mysql + - bookings_data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: ${DB_CREDENTIALS_ROOT_PASSWORD:-ClaveRoot} MYSQL_USER: ${DB_CREDENTIALS_USER:-user} MYSQL_PASSWORD: ${DB_CREDENTIALS_PASSWORD:-password} MYSQL_DATABASE: ${DB_BOOKINGS_DATABASE} MYSQL_ROOT_HOST: "%" - - ####################################### - # Cliente Angular - ####################################### - RoomsBooking-Web: - image: roomsbooking-web-image - build: - context: ./angular/RestClient - dockerfile: ./Dockerfile - restart: unless-stopped - ports: - - 4200:80 - networks: - - kong-net - depends_on: - - RoomsBooking-database diff --git a/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java b/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java index 57f661eb5b822e0172b7f94e036bd3c1c2ac95b1..b41d97f2763444174087dd536a05b6775f0e5522 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java @@ -55,14 +55,14 @@ public class UserAPI { */ public User registerUser(RegisterRequest registerRequest) { String url = USER_API_URL; - System.out.println(url + " " + registerRequest); - ResponseEntity<User> userResponse = restTemplate.postForEntity(url, registerRequest, User.class); - if (!userResponse.getStatusCode().is2xxSuccessful()) { - String errorMessage = "Failed to register user: " + userResponse.getStatusCode() + ". " + userResponse.getBody(); - throw new HttpClientErrorException(userResponse.getStatusCode(), errorMessage); + try { + ResponseEntity<User> userResponse = restTemplate.postForEntity(url, registerRequest, User.class); + return userResponse.getBody(); + } catch (HttpClientErrorException ex) { + if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Register failed"); + throw ex; } - - return userResponse.getBody(); } /** diff --git a/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java b/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java index ceda96efbbd023be438440d5bd926a73f61ca640..f84a840e5cf18b51da4e5f454e9d9d8433a0d5f8 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.HttpClientErrorException; import com.uva.api.auth.models.auth.LoginRequest; import com.uva.api.auth.models.auth.RegisterRequest; @@ -25,26 +24,12 @@ public class AuthController { @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) { - try { - return authService.login(loginRequest); - } catch (HttpClientErrorException e) { - if (e.getStatusCode() == HttpStatus.FORBIDDEN) { - return new ResponseEntity<>(e.getMessage(), HttpStatus.FORBIDDEN); - } - } - return new ResponseEntity<>("Algo no fue bien", HttpStatus.UNAUTHORIZED); + return authService.login(loginRequest); } @PostMapping("/register") public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest) { - try { - return authService.register(registerRequest); - } catch (HttpClientErrorException e) { - if (e.getStatusCode() == HttpStatus.CONFLICT) - return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT); - } - - return new ResponseEntity<>("Algo no fue bien", HttpStatus.UNAUTHORIZED); + return authService.register(registerRequest); } @PostMapping("/password") diff --git a/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java b/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..f82af24455cff5009132f7f8a16f7cd36d279f4a --- /dev/null +++ b/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,32 @@ +package com.uva.api.auth.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.client.HttpClientErrorException; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(HttpClientErrorException.class) + public ResponseEntity<Map<String, Object>> handleHttpClientErrorException(HttpClientErrorException ex) { + Map<String, Object> body = new HashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", ex.getMessage()); + + return new ResponseEntity<>(body, ex.getStatusCode()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity<Map<String, Object>> handleGeneralException(Exception ex) { + Map<String, Object> body = new HashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", "An unexpected error occurred: " + ex.getMessage()); + + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java b/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java index 144975862276370d524ff23ec596c22cf5d7474c..5cf22ae7ab1421418aa9feebd7b22fe9728f696d 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java @@ -79,7 +79,7 @@ public class AuthService { public ResponseEntity<?> changePassword(String token, String actualPass, String newPass) { JwtData decoded = jwtUtil.decodeToken(token); if (decoded == null) - return new ResponseEntity<>(HttpStatus.FORBIDDEN); + throw new HttpClientErrorException(HttpStatus.FORBIDDEN); String email = decoded.getEmail(); User user = getUser(email, actualPass); @@ -87,23 +87,22 @@ public class AuthService { boolean changePasswordAllowed = decoded.isAdmin() || user != null; if (user != null && !validStrings(actualPass, newPass)) - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - - if (changePasswordAllowed) { - // Actualizamos la nueva - String hashPass = SecurityUtils.encrypt(newPass); - userAPI.changePassword(user, hashPass); - // Hacemos un login con los nuevos datos - return login(new LoginRequest(email, newPass)); - } else { - return new ResponseEntity<>("Invalid credentials", HttpStatus.FORBIDDEN); - } + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); + + if (!changePasswordAllowed) + throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "Invalid credentials"); + + // Actualizamos la nueva + String hashPass = SecurityUtils.encrypt(newPass); + userAPI.changePassword(user, hashPass); + // Hacemos un login con los nuevos datos + return login(new LoginRequest(email, newPass)); } public ResponseEntity<?> deleteUser(String token, int id, String password) { JwtData decoded = jwtUtil.decodeToken(token); if (decoded == null) - return new ResponseEntity<>(HttpStatus.FORBIDDEN); + throw new HttpClientErrorException(HttpStatus.FORBIDDEN); String email = decoded.getEmail(); User user = getUser(email, password); @@ -112,13 +111,12 @@ public class AuthService { || (user != null && user.getId() == id); if (user != null && !validStrings(password)) - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); - if (changePasswordAllowed) { - userAPI.deleteUser(user); - return new ResponseEntity<>(HttpStatus.OK); - } else { - return new ResponseEntity<>("Invalid credentials", HttpStatus.FORBIDDEN); - } + if (!changePasswordAllowed) + throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "Invalid credentials"); + + userAPI.deleteUser(user); + return ResponseEntity.ok(user); } } diff --git a/java/services/auth/src/main/resources/application.properties b/java/services/auth/src/main/resources/application.properties index 992556c7e2cc56abc8306fde1f8f2a4ba38db817..14e05ddd3d5b195e2eaaadfa33453d0a783b674d 100644 --- a/java/services/auth/src/main/resources/application.properties +++ b/java/services/auth/src/main/resources/application.properties @@ -2,9 +2,9 @@ spring.application.name=authentication server.port=8101 security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot -# 1h in seconds +# Tiempo de validez para tokens para usuarios (externos) 1h in seconds security.jwt.external.expiration=3600 -# 10min in seconds +# Tiempo de validez para tokens para servicios (internos) 10min in seconds security.jwt.internal.expiration=600 security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java b/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java index 6e916340eda652e42cd7136ed29a49834e9eebfa..a17bf87c2b8f05fdda222f6953cda45989cb5ebe 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java @@ -14,12 +14,15 @@ public class UserApi { @Autowired private RestTemplate restTemplate; - @Value("${services.external.users.url}") - private String USERS_API_URL; + @Value("${services.external.clients.url}") + private String CLIENTS_API_URL; + + @Value("${services.external.managers.url}") + private String MANAGERS_API_URL; public boolean existsClientById(int id) { try { - String url = USERS_API_URL + "/clients/{id}"; + String url = CLIENTS_API_URL + "/{id}"; ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); return response.getStatusCode() == HttpStatus.OK; } catch (HttpClientErrorException ex) { @@ -32,7 +35,7 @@ public class UserApi { public boolean existsManagerById(int id) { try { - String url = USERS_API_URL + "/managers/{id}"; + String url = MANAGERS_API_URL + "/{id}"; ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); return response.getStatusCode() == HttpStatus.OK; } catch (HttpClientErrorException ex) { diff --git a/java/services/bookings/src/main/resources/application.properties b/java/services/bookings/src/main/resources/application.properties index 1c5908a11482969054610ea8288ec08236302981..9198f9718981af51cc83c4bd5ae8ee6150e86a52 100644 --- a/java/services/bookings/src/main/resources/application.properties +++ b/java/services/bookings/src/main/resources/application.properties @@ -7,8 +7,10 @@ spring.datasource.username=user spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.security.user.enabled=false +spring.security.user.name=user +spring.security.user.password=password services.internal.token.url=http://localhost:8101/token -services.external.users.url=http://localhost:8201/users +services.external.clients.url=http://localhost:8201/users/clients +services.external.managers.url=http://localhost:8201/users/managers services.external.hotels.url=http://localhost:8301/hotels \ No newline at end of file diff --git a/java/services/hotels/src/main/resources/application.properties b/java/services/hotels/src/main/resources/application.properties index c607a846e4bd47bac837013072041867c67713b4..f382a5dbf1698734a9b8a73be6438208a586a4d3 100644 --- a/java/services/hotels/src/main/resources/application.properties +++ b/java/services/hotels/src/main/resources/application.properties @@ -7,12 +7,8 @@ spring.datasource.username=user spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.security.user.enabled=false - -security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot -# 1h in millisecond -security.jwt.expiration-time=3600000 -security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui +spring.security.user.name=user +spring.security.user.password=password services.internal.token.url=http://localhost:8101/token services.external.managers.url=http://localhost:8201/users/managers diff --git a/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java b/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java index 4241b4ddddd05649435877f5f8400aed3191f476..f7e1e6feb8bb814a4e557171bff84e2889c89895 100644 --- a/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java +++ b/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.client.HttpClientErrorException; import java.time.LocalDateTime; import java.util.HashMap; @@ -21,6 +22,15 @@ public class GlobalExceptionHandler { return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } + @ExceptionHandler(HttpClientErrorException.class) + public ResponseEntity<Map<String, Object>> handleHttpClientErrorException(HttpClientErrorException ex) { + Map<String, Object> body = new HashMap<>(); + body.put("timestamp", LocalDateTime.now()); + body.put("message", ex.getMessage()); + + return new ResponseEntity<>(body, ex.getStatusCode()); + } + @ExceptionHandler(Exception.class) public ResponseEntity<Map<String, Object>> handleGeneralException(Exception ex) { Map<String, Object> body = new HashMap<>(); diff --git a/java/services/users/src/main/java/com/uva/api/users/repositories/UserRepository.java b/java/services/users/src/main/java/com/uva/api/users/repositories/UserRepository.java index 94c75261dce191531183675aedbb1eea33ed1a19..bf5e152826eadca9dd8cbb433a2720727f4372ed 100644 --- a/java/services/users/src/main/java/com/uva/api/users/repositories/UserRepository.java +++ b/java/services/users/src/main/java/com/uva/api/users/repositories/UserRepository.java @@ -8,4 +8,6 @@ import com.uva.api.users.models.User; public interface UserRepository extends JpaRepository<User, Integer> { Optional<User> findByEmail(String email); + + Boolean existsByEmail(String email); } diff --git a/java/services/users/src/main/java/com/uva/api/users/services/ClientService.java b/java/services/users/src/main/java/com/uva/api/users/services/ClientService.java index 6f5a8afb1b077c453f4961899c2c2fb318dbb84b..a0a197553b27ea17b2c351f489ba3da1d7f3d503 100644 --- a/java/services/users/src/main/java/com/uva/api/users/services/ClientService.java +++ b/java/services/users/src/main/java/com/uva/api/users/services/ClientService.java @@ -3,7 +3,6 @@ package com.uva.api.users.services; import java.time.LocalDate; import java.util.List; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @@ -42,9 +41,7 @@ public class ClientService { return client; } - public Client save(User request) { - Client client = new Client(); - BeanUtils.copyProperties(request, client); + public Client save(Client client) { // Default rol client.setRol(UserRol.CLIENT); return clientRepository.save(client); diff --git a/java/services/users/src/main/java/com/uva/api/users/services/ManagerService.java b/java/services/users/src/main/java/com/uva/api/users/services/ManagerService.java index 9c9dba2606e17727fe34d1b58ecda7c59c0e0096..cf9e95214e1ca2aee959ea5614f24b5d2b27d461 100644 --- a/java/services/users/src/main/java/com/uva/api/users/services/ManagerService.java +++ b/java/services/users/src/main/java/com/uva/api/users/services/ManagerService.java @@ -2,13 +2,10 @@ package com.uva.api.users.services; import java.util.List; -import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; - import com.uva.api.users.api.HotelApi; import com.uva.api.users.models.Manager; -import com.uva.api.users.models.User; import com.uva.api.users.repositories.ManagerRepository; import com.uva.api.users.utils.Utils; @@ -21,10 +18,8 @@ public class ManagerService { @Autowired private ManagerRepository managerRepository; - public Manager save(User request) { - Manager hm = new Manager(); - BeanUtils.copyProperties(request, hm); - return managerRepository.save(hm); + public Manager save(Manager manager) { + return managerRepository.save(manager); } public List<Manager> findAll() { diff --git a/java/services/users/src/main/java/com/uva/api/users/services/UserService.java b/java/services/users/src/main/java/com/uva/api/users/services/UserService.java index bf2d04f4e4abb3718566c960abed6e55d3539c01..23125b307f4ae9d974511d2693ff4185c7bf5cde 100644 --- a/java/services/users/src/main/java/com/uva/api/users/services/UserService.java +++ b/java/services/users/src/main/java/com/uva/api/users/services/UserService.java @@ -4,10 +4,14 @@ import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; import com.uva.api.users.models.AuthDTO; +import com.uva.api.users.models.Client; +import com.uva.api.users.models.Manager; import com.uva.api.users.models.User; import com.uva.api.users.models.UserRol; import com.uva.api.users.repositories.UserRepository; @@ -47,6 +51,9 @@ public class UserService { } public ResponseEntity<User> registerNewUser(AuthDTO request) { + if (userRepository.existsByEmail(request.getEmail())) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); + User user = new User(); BeanUtils.copyProperties(request, user); @@ -62,12 +69,16 @@ public class UserService { break; case HOTEL_ADMIN: - user = managerService.save(user); + Manager manager = new Manager(); + BeanUtils.copyProperties(request, manager); + user = managerService.save(manager); break; case CLIENT: // By default default: - user = clientService.save(user); + Client client = new Client(); + BeanUtils.copyProperties(request, client); + user = clientService.save(client); break; } return ResponseEntity.ok(user); diff --git a/java/services/users/src/main/resources/application.properties b/java/services/users/src/main/resources/application.properties index 6ad30a8252d53371d2accfd475e783772d1ea2ca..c680e76dd5d9f7de5d65d8c74c9da2c14cb6941c 100644 --- a/java/services/users/src/main/resources/application.properties +++ b/java/services/users/src/main/resources/application.properties @@ -10,11 +10,6 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.security.user.name=user spring.security.user.password=password -security.jwt.secret-key=MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot -# 1h in millisecond -security.jwt.expiration-time=3600000 -security.jwt.kid=cYz3kNRLAirxVhHXQ5rh5xKrOwHwZVui - services.internal.token.url=http://localhost:8101/token/ services.external.hotels.url=http://localhost:8301/hotels services.external.bookings.url=http://localhost:8401/bookings \ No newline at end of file diff --git a/kong_config.json b/kong_config.json new file mode 100644 index 0000000000000000000000000000000000000000..2754cccec0e3c556e09db59dede08bec7463183e --- /dev/null +++ b/kong_config.json @@ -0,0 +1 @@ +{"createdUser":null,"updatedUser":null,"id":1,"name":"Practica","kong_node_name":"Kong","kong_node_url":"http://kong:8001","kong_version":"3.9.0","data":{"services":[{"retries":5,"tags":[],"created_at":1735150914,"updated_at":1735150914,"name":"Users","port":8201,"id":"1d1ce8d9-aa4b-4576-8d91-00326d00c26e","client_certificate":null,"write_timeout":60000,"connect_timeout":60000,"read_timeout":60000,"protocol":"http","enabled":true,"ca_certificates":null,"tls_verify":null,"path":"/users","tls_verify_depth":null,"host":"users-api","extras":{"id":1,"service_id":"1d1ce8d9-aa4b-4576-8d91-00326d00c26e","kong_node_id":"1","description":null,"tags":null,"createdAt":"2024-12-25T18:21:54.000Z","updatedAt":"2024-12-25T18:21:54.000Z","createdUser":null,"updatedUser":null}},{"retries":5,"tags":[],"created_at":1735151821,"updated_at":1735151821,"name":"Hotels","port":8301,"id":"37407f92-1d9f-4d88-b80f-0bc9a3fb57fd","client_certificate":null,"write_timeout":60000,"connect_timeout":60000,"read_timeout":60000,"protocol":"http","enabled":true,"ca_certificates":null,"tls_verify":null,"path":"/hotels","tls_verify_depth":null,"host":"hotels-api","extras":{"id":3,"service_id":"37407f92-1d9f-4d88-b80f-0bc9a3fb57fd","kong_node_id":"1","description":null,"tags":null,"createdAt":"2024-12-25T18:37:01.000Z","updatedAt":"2024-12-25T18:37:01.000Z","createdUser":null,"updatedUser":null}},{"retries":5,"tags":[],"created_at":1735151037,"updated_at":1735151134,"name":"Auth","port":8101,"id":"60e76e38-63f1-4fd7-b10c-51456722a312","client_certificate":null,"write_timeout":60000,"connect_timeout":60000,"read_timeout":60000,"protocol":"http","enabled":true,"ca_certificates":null,"tls_verify":null,"path":"/auth","tls_verify_depth":null,"host":"auth-api","extras":{"id":2,"service_id":"60e76e38-63f1-4fd7-b10c-51456722a312","kong_node_id":"1","description":null,"tags":null,"createdAt":"2024-12-25T18:23:57.000Z","updatedAt":"2024-12-25T18:25:34.000Z","createdUser":null,"updatedUser":null}},{"retries":5,"tags":[],"created_at":1735151922,"updated_at":1735151922,"name":"Bookings","port":8401,"id":"ae250c19-10c3-4ed2-8cdb-d62497721da2","client_certificate":null,"write_timeout":60000,"connect_timeout":60000,"read_timeout":60000,"protocol":"http","enabled":true,"ca_certificates":null,"tls_verify":null,"path":"/bookings","tls_verify_depth":null,"host":"bookings-api","extras":{"id":4,"service_id":"ae250c19-10c3-4ed2-8cdb-d62497721da2","kong_node_id":"1","description":null,"tags":null,"createdAt":"2024-12-25T18:38:42.000Z","updatedAt":"2024-12-25T18:38:42.000Z","createdUser":null,"updatedUser":null}}],"routes":[{"tags":null,"created_at":1735151865,"updated_at":1735151865,"name":"hotels","destinations":null,"strip_path":true,"hosts":null,"methods":["GET","POST","PUT","PATCH","DELETE","OPTIONS"],"service":{"id":"37407f92-1d9f-4d88-b80f-0bc9a3fb57fd"},"preserve_host":false,"regex_priority":0,"headers":null,"paths":["/api/hotels"],"request_buffering":true,"response_buffering":true,"protocols":["http","https"],"sources":null,"id":"17bc07be-9af8-499f-9ec6-716504cd801f","path_handling":"v1","https_redirect_status_code":426,"snis":null},{"tags":null,"created_at":1735151081,"updated_at":1735151965,"name":"Auth","destinations":null,"strip_path":true,"hosts":null,"methods":["OPTIONS","POST","DELETE"],"service":{"id":"60e76e38-63f1-4fd7-b10c-51456722a312"},"preserve_host":false,"regex_priority":0,"headers":null,"paths":["/api/auth"],"request_buffering":true,"response_buffering":true,"protocols":["http","https"],"sources":null,"id":"4f7e698c-aed6-4919-8d72-a24ce6a2a333","path_handling":"v1","https_redirect_status_code":426,"snis":null},{"tags":null,"created_at":1735151945,"updated_at":1735151945,"name":"Bookings","destinations":null,"strip_path":true,"hosts":null,"methods":["GET","POST","PATCH","PUT","DELETE","OPTIONS"],"service":{"id":"ae250c19-10c3-4ed2-8cdb-d62497721da2"},"preserve_host":false,"regex_priority":0,"headers":null,"paths":["/api/bookings"],"request_buffering":true,"response_buffering":true,"protocols":["http","https"],"sources":null,"id":"810bbb6d-a10d-416d-b5db-a121762981fa","path_handling":"v1","https_redirect_status_code":426,"snis":null},{"tags":null,"created_at":1735150976,"updated_at":1735150976,"name":"Usuarios","destinations":null,"strip_path":true,"hosts":null,"methods":["OPTIONS","GET","POST","DELETE","PUT","PATCH"],"service":{"id":"1d1ce8d9-aa4b-4576-8d91-00326d00c26e"},"preserve_host":false,"regex_priority":0,"headers":null,"paths":["/api/users"],"request_buffering":true,"response_buffering":true,"protocols":["http","https"],"sources":null,"id":"df0fefa6-7df8-4ae3-baed-ae58add42bbf","path_handling":"v1","https_redirect_status_code":426,"snis":null}],"consumers":[{"id":"c012b84f-63af-461f-8a8d-b8f99a2bd006","tags":[],"created_at":1735155577,"updated_at":1735155577,"custom_id":null,"username":"JWT","credentials":{"jwts":[{"rsa_public_key":null,"algorithm":"HS256","tags":null,"created_at":1735155689,"key":"9L8RmLEKMFd54WrR06CE9zoRSAGpCWeu","consumer":{"id":"c012b84f-63af-461f-8a8d-b8f99a2bd006"},"id":"493ad0f9-23e7-4e5f-b6e1-6982677ba0d6","secret":"MiClaveDeSeguridadMuyLargaParaQueNoFalleSpringBoot"}]}}],"plugins":[{"config":{"secret_is_base64":false,"key_claim_name":"kid","claims_to_verify":["exp"],"maximum_expiration":0,"uri_param_names":["jwt"],"cookie_names":[],"header_names":["Authorization"],"realm":null,"run_on_preflight":true,"anonymous":null},"tags":null,"created_at":1735155843,"updated_at":1735155843,"name":"jwt","route":{"id":"810bbb6d-a10d-416d-b5db-a121762981fa"},"consumer":null,"id":"3e302549-c51f-469d-a71d-98f30f2ddcab","protocols":["grpc","grpcs","http","https"],"instance_name":null,"service":null,"enabled":true},{"config":{"secret_is_base64":false,"key_claim_name":"kid","claims_to_verify":["exp"],"maximum_expiration":0,"uri_param_names":["jwt"],"cookie_names":[],"header_names":["Authorization"],"realm":null,"run_on_preflight":true,"anonymous":null},"tags":null,"created_at":1735156525,"updated_at":1735156585,"name":"jwt","route":{"id":"df0fefa6-7df8-4ae3-baed-ae58add42bbf"},"consumer":null,"id":"8386bf54-ede1-4f34-bb87-989ce4276e8a","protocols":["grpc","grpcs","http","https"],"instance_name":null,"service":null,"enabled":true},{"config":{"secret_is_base64":false,"key_claim_name":"kid","claims_to_verify":["exp"],"maximum_expiration":0,"uri_param_names":["jwt"],"cookie_names":[],"header_names":["Authorization"],"realm":null,"run_on_preflight":true,"anonymous":null},"tags":null,"created_at":1735156492,"updated_at":1735156600,"name":"jwt","route":{"id":"17bc07be-9af8-499f-9ec6-716504cd801f"},"consumer":null,"id":"9ac23748-1964-4038-8cdc-b2db799010a6","protocols":["grpc","grpcs","http","https"],"instance_name":null,"service":null,"enabled":true}],"acls":[],"upstreams":[],"certificates":[],"snis":[]},"createdAt":"2024-12-25T19:59:28.000Z","updatedAt":"2024-12-25T19:59:28.000Z"} \ No newline at end of file diff --git a/poblate/Dockerfile b/poblate/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..29f6ea71764e850a3695168ca57b2231b7dd4527 --- /dev/null +++ b/poblate/Dockerfile @@ -0,0 +1,11 @@ +FROM node:20 + +# Establecer el directorio de trabajo +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . + +CMD ["npm", "run", "prod", "--", "-d"] \ No newline at end of file diff --git a/poblate/index.js b/poblate/index.js index 1a3b3396858e487e66561cbf14ac74935a11ecb1..1a95be42b34a0cedb0dd753a3a0a6b316451f299 100644 --- a/poblate/index.js +++ b/poblate/index.js @@ -10,6 +10,7 @@ const args = process.argv; const isProduction = args.includes("--prod"); const DEBUG = args.includes("--debug") || args.includes("-d"); const FORCE = args.includes("--force") || args.includes("-f"); +const ERROR = args.includes("--error") || args.includes("-e"); const env = (isProduction ? prod : dev).env; const { authApi, hotelsApi, bookingsApi } = env; @@ -33,7 +34,7 @@ function genDates(ref = new Date()) { // After const afterStart = new Date(ref); - afterStart.setDate(ref.getDate() + 14); // Restar 2 semanas + afterStart.setDate(ref.getDate() + 14); // Sumar 2 semanas const afterEnd = new Date(afterStart); afterEnd.setDate(afterStart.getDate() + 2); @@ -57,18 +58,23 @@ function getRandomItem(a = []) { const savePost = async (data, first, second = "") => { try { try { + debug("Trying to register user", data); return await axios.post(first, data); } catch (error) { - console.error("ERROR Al REGISTRO, SE PROCEDE A INTENTAR ACCEDER"); + debug("Trying to log user", data); + debug("ERROR:", ERROR ? error : error.data ?? error.cause); + debug("ERROR Al REGISTRO, SE PROCEDE A INTENTAR ACCEDER"); + const response = await axios.post(second, data); if (!FORCE) { console.log("Parece que ya hay datos en el sistema"); process.exit(0); } - return await axios.post(second, data); + return response; } } catch (error) { console.error("ERROR Al LOGIN"); console.error("\nNo se ha podido comunicar con el servicio de auth"); + debug("ERROR:", ERROR ? error : error.data ?? error.cause); process.exit(-1); } }; @@ -79,6 +85,7 @@ async function register(user) { `${authApi}/register`, `${authApi}/login` ); + debug("User identified successful"); const decoded = jwtDecode(data.token); user.id = decoded.id; user.token = data.token; @@ -100,22 +107,21 @@ const addUsers = async () => { const insertHotel = async ({ manager, hotel }) => { try { - const { data } = await axios.post( - hotelsApi, - { - ...hotel, - managerId: manager.id, + const body = { + ...hotel, + managerId: manager.id, + }; + debug("Trying to add booking", body); + const { data } = await axios.post(hotelsApi, body, { + headers: { + Authorization: `Bearer ${manager.token}`, }, - { - headers: { - Authorization: `Bearer ${manager.token}`, - }, - } - ); + }); + debug("Hotel added successful"); return data; - } catch (e) { - // console.error(e); + } catch (error) { console.error("ERROR Al INSERTAR HOTEL"); + debug("ERROR:", ERROR ? error : error.data ?? error.cause); process.exit(-1); } }; @@ -131,15 +137,17 @@ async function addHotels(managers) { const insertBookings = async (booking, token) => { try { + debug("Trying to add booking", booking); const { data } = await axios.post(bookingsApi, booking, { headers: { Authorization: `Bearer ${token}`, }, }); + debug("Booking added successful"); return data; - } catch (e) { - // console.error(e); - console.error("ERROR Al INSERTAR HOTEL"); + } catch (error) { + console.error("ERROR Al INSERTAR RESERVA"); + debug("ERROR:", ERROR ? error : error.data ?? error.cause); process.exit(-1); } };