package com.banesco.common.infraestructure.helpers; import com.banesco.common.domain.dto.bian.device.BianDevice; import com.banesco.common.domain.model.Device; import com.banesco.common.infraestructure.config.DeviceTypeValues; import javax.servlet.http.HttpServletRequest; import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; public class DeviceHelper { private boolean readDeviceFromRequest; private final String DESKTOP = "Desktop"; private static final Logger logger = Logger.getLogger(DeviceHelper.class.getName()); /** * Nombres de cabeceras HTTP que pueden contener la dirección IP del * cliente. Se revisan en orden para encontrar la IP real considerando * proxies y balanceadores. */ private final String[] IP_HEADER_NAMES = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED", "HTTP_X_CLUSTER_CLIENT_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED", "HTTP_VIA", "REMOTE_ADDR" }; public DeviceHelper() { // Lee la propiedad del sistema. Si no existe, asume false por defecto. String readDeviceProperty = System.getProperty("api.server-request.read-device"); this.readDeviceFromRequest = readDeviceProperty == null ? false : Boolean.parseBoolean(readDeviceProperty); } /** * Obtiene la dirección IP remota del cliente a partir de la solicitud HTTP. * Revisa varias cabeceras HTTP que podrían contener la IP real del cliente * considerando proxies y balanceadores de carga. * * @param request La solicitud HTTP * @return La dirección IP remota del cliente, o "0.0.0.0" si la solicitud * es nula */ public String getRemoteIP(HttpServletRequest request) { if (request == null) { return "0.0.0.0"; } for (String header : IP_HEADER_NAMES) { String ipList = request.getHeader(header); if (!StringHelper.isEmpty(ipList) && !"unknown".equalsIgnoreCase(ipList)) { return ipList.split(",")[0]; } } return request.getRemoteAddr(); } /** * Llena la información del dispositivo remoto del cliente en el objeto * Device. Si se configura readDeviceFromRequest como true, siempre se * obtendrá la información del dispositivo desde la solicitud HTTP, * independientemente de si ya existe. * * @param servletRequest La solicitud HTTP * @param device El objeto Device existente a actualizar o null para crear * uno nuevo * @return El objeto Device con la información del cliente actualizada */ public Device fillRemoteDevice(HttpServletRequest servletRequest, Device device) { if (device == null) { device = new Device(); } if (StringHelper.isEmpty(device.getIpAddress()) || readDeviceFromRequest) { device.setIpAddress(getRemoteIP(servletRequest)); String userAgent = servletRequest.getHeader("user-agent"); if (userAgent == null) { device.setType(DESKTOP); device.setDescription(DESKTOP); } else { device.setDescription(userAgent); if (userAgent.toLowerCase().contains("mobi")) { device.setType("Mobile"); } else { device.setType(DESKTOP); } } } return device; } /** * Mapea el tipo de dispositivo desde el objeto BianDevice al enum DeviceTypeValues. * * @param device * @return */ public static DeviceTypeValues getDeviceTypeValue( BianDevice device ) { var deviceTypeValue = DeviceTypeValues.Unknown; if (!Objects.isNull(device) && !StringHelper.isEmpty(device.getDeviceType())) { try { deviceTypeValue = DeviceTypeValues.valueOf(device.getDeviceType()); } catch (Exception e) { logger.log(Level.WARNING, "Error Mapping Device Type: %s -> %s".formatted(device.getDeviceType(), e.getMessage())); } } return deviceTypeValue; } /** * Valida si el tipo de dispositivo es válido según el enum DeviceTypeValues. * * @param type * @return */ public static boolean isValidDeviceType(String type) { for (DeviceTypeValues deviceType : DeviceTypeValues.values()) { if (deviceType.name().equals(type)) { return true; } } return false; } /** * Obtiene una cadena con todos los tipos de dispositivos válidos. * * @return */ public static String getDeviceTypes() { return Stream.of(DeviceTypeValues.values()).map(Enum::name).collect( Collectors.joining(", ") ); } }