Add: CORS handling for dev profile. Fix: custom dates on socios, ingresos and gastos. Add: new endpoint for socios dropdown on ingresos
This commit is contained in:
5
TODO
5
TODO
@@ -1,4 +1,5 @@
|
||||
POR HACER --------------------------------
|
||||
- implementar urlParams para filtros
|
||||
- documentación
|
||||
- mail wrapper
|
||||
|
||||
@@ -8,7 +9,9 @@ RESUELTO ---------------------------------
|
||||
- aceptar solicitudes LE/Colab (sobre todo por crear preusers)
|
||||
- mejorar queries para no filtrar en memoria -> IMPOSIBLE CON ENDPOINTS INTERNOS DE CORE: RESUELTO CON CACHING
|
||||
- normalizar el uso de services y repositories desde otros services y repositories
|
||||
- implementar urlParams para filtros -> NO RESUELTO (DEPRECATED)
|
||||
- sistema comun de errores en back & front
|
||||
- nombre del requester
|
||||
- cambiar contraseña (?)
|
||||
- todos los socios en dropdown ingresos
|
||||
- validacion LE/COlab
|
||||
- createdAt custom en ing,gastos
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.miarma.backlib.filter;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Component
|
||||
public class RequestLoggingFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain
|
||||
) throws ServletException, IOException {
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
|
||||
log.info("({}) {} {} -> {} ({} ms)",
|
||||
request.getRemoteAddr(),
|
||||
request.getMethod(),
|
||||
request.getRequestURI(),
|
||||
response.getStatus(),
|
||||
duration
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.miarma.backlib.http;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@Profile("dev") // esto asegura que solo se cargue en dev
|
||||
public class DevCorsConfig {
|
||||
|
||||
@Bean
|
||||
public WebMvcConfigurer corsConfigurer() {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedOrigins("http://localhost:3000") // tu frontend React
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||
.allowCredentials(true)
|
||||
.allowedHeaders("*");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/exception/NotFoundException.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/exception/UnauthorizedException.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/exception/ValidationException.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/filter/RequestLoggingFilter.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/http/DevCorsConfig.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/http/GlobalExceptionHandler.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/http/RestAccessDeniedHandler.java
|
||||
/home/jomaa/git/miarma-backend/backlib/src/main/java/net/miarma/backlib/http/RestAuthEntryPoint.java
|
||||
|
||||
10
build-upload.sh
Executable file
10
build-upload.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd core/
|
||||
mvn clean package
|
||||
cd ..
|
||||
cd huertos/
|
||||
mvn clean package
|
||||
cd ..
|
||||
scp core/target/core-1.0.0.jar jomaa@10.0.0.254:/home/jomaa/transfer
|
||||
scp huertos/target/huertos-1.0.0.jar jomaa@10.0.0.254:/home/jomaa/transfer
|
||||
@@ -12,6 +12,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -20,19 +23,26 @@ public class SecurityConfig {
|
||||
private final JwtFilter jwtFilter;
|
||||
private final RestAuthEntryPoint authEntryPoint;
|
||||
private final RestAccessDeniedHandler accessDeniedHandler;
|
||||
private final CorsConfigurationSource corsConfigurationSource;
|
||||
|
||||
public SecurityConfig(
|
||||
JwtFilter jwtFilter,
|
||||
RestAuthEntryPoint authEntryPoint,
|
||||
RestAccessDeniedHandler accessDeniedHandler
|
||||
RestAccessDeniedHandler accessDeniedHandler,
|
||||
Optional<CorsConfigurationSource> corsConfigurationSource
|
||||
) {
|
||||
this.jwtFilter = jwtFilter;
|
||||
this.authEntryPoint = authEntryPoint;
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
this.corsConfigurationSource = corsConfigurationSource.orElse(null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
if (corsConfigurationSource != null) {
|
||||
http.cors(Customizer.withDefaults());
|
||||
}
|
||||
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
|
||||
@@ -5,12 +5,16 @@ import net.miarma.backlib.http.RestAccessDeniedHandler;
|
||||
import net.miarma.backlib.http.RestAuthEntryPoint;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
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.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@@ -20,19 +24,26 @@ public class SecurityConfig {
|
||||
private final HuertosJwtFilter jwtFilter;
|
||||
private final RestAuthEntryPoint authEntryPoint;
|
||||
private final RestAccessDeniedHandler accessDeniedHandler;
|
||||
private final CorsConfigurationSource corsConfigurationSource;
|
||||
|
||||
public SecurityConfig(
|
||||
HuertosJwtFilter jwtFilter,
|
||||
RestAuthEntryPoint authEntryPoint,
|
||||
RestAccessDeniedHandler accessDeniedHandler
|
||||
RestAccessDeniedHandler accessDeniedHandler,
|
||||
Optional<CorsConfigurationSource> corsConfigurationSource
|
||||
) {
|
||||
this.jwtFilter = jwtFilter;
|
||||
this.authEntryPoint = authEntryPoint;
|
||||
this.accessDeniedHandler = accessDeniedHandler;
|
||||
this.corsConfigurationSource = corsConfigurationSource.orElse(null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
if (corsConfigurationSource != null) {
|
||||
http.cors(Customizer.withDefaults());
|
||||
}
|
||||
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||
|
||||
@@ -31,7 +31,7 @@ public class MemberController {
|
||||
@GetMapping
|
||||
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
|
||||
public ResponseEntity<List<MemberDto>> getAll() {
|
||||
return ResponseEntity.ok(memberService.getAll((byte)1));
|
||||
return ResponseEntity.ok(memberService.getAll());
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
@@ -45,6 +45,12 @@ public class MemberController {
|
||||
);
|
||||
}
|
||||
|
||||
@GetMapping("/dropdown")
|
||||
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
|
||||
public ResponseEntity<List<DropdownDto>> getDropdown() {
|
||||
return ResponseEntity.ok(memberService.getDropdown());
|
||||
}
|
||||
|
||||
@GetMapping("/{user_id:[0-9a-fA-F\\-]{36}}")
|
||||
@PreAuthorize("hasAnyRole('HUERTOS_ROLE_ADMIN', 'HUERTOS_ROLE_DEV')")
|
||||
public ResponseEntity<MemberDto> getById(@PathVariable("user_id") UUID userId) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.miarma.backend.huertos.dto;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record DropdownDto(UUID userId, Integer memberNumber, String displayName) {
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public class ExpenseDto {
|
||||
private String supplier;
|
||||
private String invoice;
|
||||
private Byte type;
|
||||
private Instant createdAt;
|
||||
|
||||
public String getConcept() {
|
||||
return concept;
|
||||
@@ -51,6 +52,14 @@ public class ExpenseDto {
|
||||
public void setType(Byte type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
@@ -59,6 +68,8 @@ public class ExpenseDto {
|
||||
private BigDecimal amount;
|
||||
private String supplier;
|
||||
private String invoice;
|
||||
private Byte type;
|
||||
private Instant createdAt;
|
||||
|
||||
public UUID getExpenseId() {
|
||||
return expenseId;
|
||||
@@ -115,8 +126,5 @@ public class ExpenseDto {
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
private Byte type;
|
||||
private Instant createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class IncomeDto {
|
||||
private BigDecimal amount;
|
||||
private Byte type;
|
||||
private Byte frequency;
|
||||
private Instant createdAt;
|
||||
|
||||
public Integer getMemberNumber() {
|
||||
return memberNumber;
|
||||
@@ -60,6 +61,14 @@ public class IncomeDto {
|
||||
public void setFrequency(Byte frequency) {
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.miarma.backend.huertos.mapper;
|
||||
|
||||
import net.miarma.backend.huertos.dto.DropdownDto;
|
||||
import net.miarma.backend.huertos.dto.MemberDto;
|
||||
|
||||
public class DropdownDtoMapper {
|
||||
public static DropdownDto toDto(MemberDto dto) {
|
||||
return new DropdownDto(dto.user().getUserId(), dto.metadata().getMemberNumber(), dto.user().getDisplayName());
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class ExpenseMapper {
|
||||
entity.setSupplier(dto.getSupplier());
|
||||
entity.setInvoice(dto.getInvoice());
|
||||
entity.setType(dto.getType());
|
||||
entity.setCreatedAt(dto.getCreatedAt());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public class IncomeMapper {
|
||||
entity.setAmount(dto.getAmount());
|
||||
entity.setType(dto.getType());
|
||||
entity.setFrequency(dto.getFrequency());
|
||||
entity.setCreatedAt(dto.getCreatedAt());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,9 +46,11 @@ public class ExpenseService {
|
||||
if (expense.getInvoice() == null || expense.getInvoice().isBlank()) {
|
||||
throw new ValidationException("invoice", "La factura es obligatoria");
|
||||
}
|
||||
if (expense.getCreatedAt() == null) {
|
||||
expense.setCreatedAt(Instant.now());
|
||||
}
|
||||
|
||||
expense.setExpenseId(UUID.randomUUID());
|
||||
expense.setCreatedAt(Instant.now());
|
||||
|
||||
return expenseRepository.save(expense);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.miarma.backend.huertos.service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -56,9 +57,11 @@ public class IncomeService {
|
||||
if (income.getAmount() == null || income.getAmount().signum() <= 0) {
|
||||
throw new ValidationException("amount", "La cantidad debe ser positiva");
|
||||
}
|
||||
if (income.getCreatedAt() == null) {
|
||||
income.setCreatedAt(Instant.now());
|
||||
}
|
||||
|
||||
income.setIncomeId(UUID.randomUUID());
|
||||
income.setCreatedAt(Instant.now());
|
||||
|
||||
return incomeRepository.save(income);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import net.miarma.backend.huertos.client.HuertosWebClient;
|
||||
import net.miarma.backend.huertos.dto.*;
|
||||
import net.miarma.backend.huertos.mapper.DropdownDtoMapper;
|
||||
import net.miarma.backend.huertos.mapper.RequestMapper;
|
||||
import net.miarma.backend.huertos.mapper.UserMetadataMapper;
|
||||
import net.miarma.backend.huertos.mapper.IncomeMapper;
|
||||
@@ -52,8 +53,8 @@
|
||||
}
|
||||
|
||||
@Cacheable("members")
|
||||
public List<MemberDto> getAll(Byte serviceId) {
|
||||
List<UserWithCredentialDto> all = huertosWebClient.getAllUsersWithCredentials(serviceId);
|
||||
public List<MemberDto> getAll() {
|
||||
List<UserWithCredentialDto> all = huertosWebClient.getAllUsersWithCredentials((byte)1);
|
||||
|
||||
return all.stream()
|
||||
.filter(uwc -> metadataService.existsById(uwc.user().getUserId()))
|
||||
@@ -125,21 +126,21 @@
|
||||
}
|
||||
|
||||
public MemberDto getByMemberNumber(Integer memberNumber) {
|
||||
return getAll((byte)1).stream()
|
||||
return getAll().stream()
|
||||
.filter(dto -> dto.metadata().getMemberNumber().equals(memberNumber))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NotFoundException("No hay socio con ese número"));
|
||||
}
|
||||
|
||||
public MemberDto getByPlotNumber(Integer plotNumber) {
|
||||
return getAll((byte)1).stream()
|
||||
return getAll().stream()
|
||||
.filter(dto -> dto.metadata().getPlotNumber().equals(plotNumber))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NotFoundException("No hay socio con ese huerto"));
|
||||
}
|
||||
|
||||
public MemberDto getByDni(String dni) {
|
||||
return getAll((byte)1).stream()
|
||||
return getAll().stream()
|
||||
.filter(dto -> dni.equals(dto.metadata().getDni()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NotFoundException("No hay socio con ese DNI"));
|
||||
@@ -156,7 +157,7 @@
|
||||
}
|
||||
|
||||
public Boolean hasCollaborator(Integer memberNumber) {
|
||||
List<MemberDto> all = getAll((byte)1);
|
||||
List<MemberDto> all = getAll();
|
||||
|
||||
var member = all.stream()
|
||||
.filter(dto -> dto.metadata().getMemberNumber().equals(memberNumber))
|
||||
@@ -189,4 +190,10 @@
|
||||
UUID userId = metadataService.getByMemberNumber(memberNumber).getUserId();
|
||||
return requestService.hasGreenhouseRequest(userId);
|
||||
}
|
||||
|
||||
public List<DropdownDto> getDropdown() {
|
||||
return getAll().stream()
|
||||
.map(DropdownDtoMapper::toDto)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public class RequestValidator {
|
||||
|
||||
if (requestType == 2) {
|
||||
if (metadata.getPlotNumber() == null) {
|
||||
throw new BadRequestException("El colaborador debe tener parcela");
|
||||
throw new BadRequestException("El colaborador debe tener huerto");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,22 @@ public class RequestValidator {
|
||||
}
|
||||
}
|
||||
|
||||
if (requestType == 0) {
|
||||
if (isBlank(metadata.getAddress())) {
|
||||
throw new ValidationException("address", "La dirección es obligatoria");
|
||||
}
|
||||
if (isBlank(metadata.getZipCode())) {
|
||||
throw new ValidationException("zipCode", "El código postal es obligatorio");
|
||||
} else if(metadata.getZipCode().length() < 5) {
|
||||
throw new ValidationException("zipCode", "El código postal debe tener 5 dígitos");
|
||||
}
|
||||
if (isBlank(metadata.getCity())) {
|
||||
throw new ValidationException("city", "La ciudad es obligatoria");
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata.getPhone() != null && !PHONE_PATTERN.matcher(metadata.getPhone()).matches()) {
|
||||
throw new ValidationException("phone", "Teléfono inválido (debe tener entre 9 y 15 dígitos, opcional +)");
|
||||
throw new ValidationException("phone", "Teléfono inválido (debe tener 9 dígitos)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user