infranete-api
1. Descripción general
Según el pom.xml, el proyecto se describe como "Infranete Api". Es la API backend central del ecosistema de microservicios Hawkers PI: expone más de 30 controladores REST organizados por dominio (pedidos, logística, marketplaces, feeds, cupones, paneles, reportes, stock) y actúa como punto de integración con múltiples marketplaces internacionales (Shopee, Decathlon, Miravia, TheIconic, Zalando, AliExpress), con Dynamics 365, y con Salesforce Commerce Cloud (SFCC). Es, con diferencia, el proyecto más grande y con más superficie de integración documentado en esta serie.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | infranete-api |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, API web de larga duración) |
| Módulos | No aplica (proyecto de módulo único, pero con más de 100 clases) |
3. Arquitectura y diseño
API REST clásica sobre 5 bases de datos PostgreSQL independientes, con seguridad OAuth2/JWT (Auth0) condicionada por entorno, más tres jobs de sincronización que también se ejecutan una vez al arrancar la aplicación (además de por cron).
Paquetes principales bajo com.hawkersco.infraneteapi:
controller/— 31 controladores REST (.DS_Storeaparte) organizados en subpaquetes: raíz,coupons/,dynamics/,feeds/,logistics/,marketplaces/,orders/,panels/,reports/,stock/,web/.service/— capa de servicio, organizada en los mismos dominios que sus repositorios (logistics,marketplaces,sunajager,dynamics,feeds,commons,asyncmethods).respository/(sic, con el typo intencional documentado enCLAUDE.md) — repositorios Spring Data JPA, uno por base de datos:logistics,marketplaces,sunajager,dynamics,feeds.dao/— entidades JPA, organizadas igual que los repositorios.config/—LogisticsDbConfig(primario),MarketplacesDbConfig,SunaJagerDbConfig,DynamicsDbConfig,FeedsDbConfig,SecurityConfig,AudienceValidator,AppConfig.job/— 8 clases de sincronización/limpieza programada.utils/— utilidades de negocio (parseo Cooper, feeds, stock por marketplace, Google Drive, etc.).model/— DTOs de request/response.- Raíz —
InfraneteApiApplication,InfraneteApiRunner(activo),CleanApiRunner(desactivado, ver hallazgo crítico en la sección 13).
flowchart TD
A[Cliente / Frontend interno] -->|JWT Auth0| B[SecurityConfig]
B --> C[Controladores REST por dominio]
C --> D[Servicios]
D --> E1[(logistics)]
D --> E2[(marketplaces)]
D --> E3[(sunajager)]
D --> E4[(dynamics-pro)]
D --> E5[(feeds)]
D --> F[Clientes de marketplace:<br/>Shopee, Decathlon, Miravia,<br/>TheIconic, Zalando, SFCC, Dynamics]
G[InfraneteApiRunner al arrancar] --> H[OriginalFeedsSync / OrderLinesSync / ImagesFeedsSync]
I["Jobs @Scheduled"] --> H
Multi-datasource (5 bases de datos PostgreSQL)
| Config class | Prefijo de propiedad | Repositorios | DAO |
|---|---|---|---|
LogisticsDbConfig (@Primary) | spring.datasource | respository.logistics | dao.logistics |
MarketplacesDbConfig | marketplaces.datasource | respository.marketplaces | dao.marketplaces |
SunaJagerDbConfig | sunajager.datasource | respository.sunajager | dao.sunajager |
DynamicsDbConfig | dynamics.datasource | respository.dynamics | dao.dynamics |
FeedsDbConfig | feeds.datasource | respository.feeds | dao.feeds |
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter-web | API REST |
spring-boot-starter-security + spring-boot-starter-oauth2-resource-server | Autenticación/autorización JWT (Auth0) |
spring-boot-starter-data-jpa + postgresql:42.7.5 | Persistencia sobre las 5 bases de datos |
org.apache.poi / poi-ooxml:5.3.0 | Generación de ficheros Excel (reportes/catálogos) |
com.google.apis:google-api-services-sheets | Integración con Google Sheets (generación de catálogos SFCC, DANE, etc.) |
com.google.api-client / google-oauth-client-jetty | Cliente Google genérico |
org.json:json | Utilidades JSON |
com.hawkersco:pi-function-commons | Utilidades comunes |
com.hawkersco:shopee-client, sfcc-client, sfcc-commons, decathlon-client, miravia-client, zalando-client, theiconic-client, dynamics-client, sprintlogistics-client | Clientes de marketplace/ERP |
com.hawkersco:slack-client | Notificaciones de error |
com.hawkersco:pi-generate-credentials-client | Generación de credenciales para otros servicios |
spring-boot-devtools (runtime, opcional) | Recarga en caliente en desarrollo |
commons-lang3 | Utilidades varias |
5. API / Endpoints
Más de 30 controladores agrupados por dominio (rutas verificadas contra el código actual):
| Dominio | Paquete | Controladores reales |
|---|---|---|
| Núcleo | controller/ | InfraneteApiController, JobsController |
| Pedidos | controller/orders/ | OrdersCommonController, OrdersInfoController, OrderLinesCommonController, CountryController, FraudulentEmailController, OrdersNonStockController, OrdersZipErrorController, OrdersPhoneErrorController, OrdersDaneErrorController, OrdersAddressErrorController, Orders99minErrorController, OrdersAuroOnHoldController, OrdersDecathlonController |
| Marketplaces | controller/marketplaces/ | MarketplacesCommonController, ZalandoController |
| Dynamics | controller/dynamics/ | DynamicsController |
| Feeds | controller/feeds/ | GoogleFeedsController, ImagesFeedsController, RedirectFeedsController |
| Logística | controller/logistics/ | LogisticsUpdateFileController, LogisticsUpdateStockController, SfccShippingMethodsController |
| Cupones | controller/coupons/ | DiscountCodeGeneratorApiController |
| Paneles | controller/panels/ | PanelsOrdersController, PanelsStatusController |
| Reportes | controller/reports/ | ReportsCommonController |
| Stock | controller/stock/ | DrStockController |
| Web/SFCC | controller/web/ | JsonSeoPlpGenerateController, StockSfccController |
Reglas de autorización por scope (ver SecurityConfig, entorno pro): SCOPE_commons (búsquedas comunes de pedidos/países), SCOPE_orders (edición/creación/cancelación de pedidos, facturas, no-stock, errores de dirección/teléfono/DANE, emails fraudulentos), SCOPE_logistics (sfccsm, dr-stock), SCOPE_coupons, SCOPE_marketplaces, SCOPE_feeds (images-feeds, redirect-feeds, google-feeds), SCOPE_web (stock-sfcc, json-seo-plp-generate). Rutas públicas: /api/check-domain, /jobs/token, /api/optics/*.
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
| Shopee, Decathlon (EU/AU), Miravia, TheIconic, Zalando, AliExpress | HTTP (clientes @HttpExchange o Feign, según librería) | Saliente | Sincronización de stock/pedidos por marketplace |
| Salesforce Commerce Cloud (SFCC) | HTTP (sfcc-client) | Entrante/Saliente | Generación de catálogos, actualización de stock web |
| Dynamics 365 (Commerce + F&O) | HTTP OAuth client_credentials (dynamics-client) | Entrante/Saliente | Precios, stock, clientes |
| Sprint Logistics (API v2) | HTTP | Saliente | Integración logística |
| Google Sheets/Drive | API de Google | Entrante | Generación de catálogos (SFCC, variaciones), procesamiento de imágenes de feeds |
| Cooper Vision (SFTP) | SFTP | Saliente | Pedidos de lentillas |
SFTP genérico (sftp.hawkersco.com) | SFTP | Entrante/Saliente | Feeds, imágenes, Auro |
| SMTP (Gmail) | SMTP | Saliente | Envío de correo (hawkers.contact@saldum.com) |
| Auth0 | OAuth2/JWT | Entrante | Autenticación de la propia API en entorno pro/test |
| Slack | HTTP (SlackClient) | Saliente | Notificaciones de error |
7. Configuración
En producción (application-pro.properties, plantilla versionada) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; application.properties está en .gitignore según indica el propio CLAUDE.md, pero el fichero presente en este repositorio de trabajo sí existe físicamente y contiene credenciales reales de prácticamente todas las integraciones (ver alerta de seguridad, la más severa de todo este lote de proyectos).
| Clave (prefijo) | Descripción |
|---|---|
spring.datasource.* | BD logistics (primaria) |
marketplaces.datasource.* | BD marketplaces |
sunajager.datasource.* | BD sunajager |
dynamics.datasource.* | BD dynamics-pro |
feeds.datasource.* | BD feeds |
application.environment | dev / test / pro — controla el nivel de aplicación de seguridad en SecurityConfig |
spring.security.oauth2.resourceserver.jwt.issuer-uri, auth0.audience | Configuración Auth0 |
infranete.interface | Origen permitido por CORS (http://localhost:4040 en dev) |
shopee.api.*, decathlon*.credentials.*, miravia.client.*, theiconic.api.*, zalando.*, aliexpress.*, sfcc.*, dynamics.login.*, dynamics.picustomersetup.*, sprintlogistics-v2.api.* | Credenciales de cada integración de marketplace/ERP |
dev.ftp.*, pro.ftp.*, cooper.ftp.*, auro.ftp.*, images.ftp.* | Credenciales SFTP por integración |
slack.client.url / .auth.token / .channel.id | Notificaciones Slack |
spring.mail.* | Credenciales SMTP |
sfccgenerator.*, zalando.*.drive | IDs de hojas de Google Sheets usadas como catálogo/config |
⚠️ Alerta de seguridad (severidad crítica)
El fichero src/main/resources/application.properties de este repositorio de trabajo contiene credenciales reales de prácticamente todas las integraciones externas de Hawkers: contraseñas de las 5 bases de datos PostgreSQL (todas con el mismo usuario/contraseña), la API key/secret de partner de Shopee, las claves de Mirakl de Decathlon EU y AU, el appkey/appsecret de Miravia, el client-id/client-secret de TheIconic, la clave/contraseña base de Zalando, el app-secret/generated-secret de AliExpress, el client-id/client-secret de SFCC (dos aplicaciones distintas), un client-secret OAuth de Dynamics 365 (el mismo tenant PRO ya señalado como expuesto en gio-pos-sync-service), credenciales SFTP de Cooper Vision, Auro y del servidor general de imágenes/feeds, el token de bot de Slack, y la contraseña SMTP de la cuenta hawkers.contact@saldum.com. Ninguno de estos valores se ha reproducido en este documento. Dado el alcance (prácticamente toda la superficie de integración del ecosistema Hawkers PI queda comprometida si este fichero se filtra), se recomienda con prioridad máxima:
- Rotar de forma inmediata y coordinada todas las credenciales listadas: las 5 bases de datos, Shopee, Decathlon EU/AU, Miravia, TheIconic, Zalando, AliExpress, SFCC, Dynamics 365, Cooper Vision, Auro, SFTP de imágenes, Slack y la cuenta SMTP.
- Confirmar si el fichero
application.propertiesestá realmente excluido por.gitignoreen el repositorio remoto (elCLAUDE.mdlo da por hecho) y, si no lo está o lo estuvo en el pasado, auditar el historial de Git para determinar el alcance real de la exposición. - Mover progresivamente esta configuración a un gestor de secretos centralizado en lugar de un fichero de propiedades local, dado el volumen y la criticidad de las credenciales involucradas.
8. Persistencia
Cinco bases de datos PostgreSQL independientes, cada una con su propio DataSource/EntityManagerFactory/TransactionManager (logistics como primaria): logistics, marketplaces, sunajager, dynamics-pro, feeds. Todas con *.jpa.hibernate.ddl-auto=none. No hay Flyway/Liquibase en este repositorio.
9. Procesos programados y mensajería
Al arrancar (InfraneteApiRunner, solo si application.environment=pro, una única vez por ciclo de vida del proceso)
originalFeedsSync.downloadNewFeeds() → orderLinesSync.syncOrderLines() → imagesFeedsSync.downloadAllImages().
Jobs con @Scheduled activo
| Job | Cron | Función |
|---|---|---|
ImagesFeedsSync | 0 45 23 * * ? y 0 0/30 * ? * * | Descarga de imágenes de feeds (limpieza a las 23:45, sincronización cada 30 min) |
OrdersErrorCleanJob | 0 0 3,15 * * ? | Limpieza de pedidos en estado de error, dos veces al día |
Jobs sin @Scheduled (invocados como beans desde otros puntos, no programados por cron)
MiraviaUpdateStockJob, SfccNwUpdateStockJob, SfccUpdateStockJob, ShopeeUpdateStockJob, OrderLinesSync (invocado por InfraneteApiRunner, no tiene cron propio).
Jobs con @Scheduled comentado (desactivados)
CusterIpJob (// @Scheduled(cron = "0 */5 * ? * *")), OriginalFeedsSync (//@Scheduled(cron = "0 */5 * * * ?"), invocado igualmente desde InfraneteApiRunner al arrancar).
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a las 5 bases de datos PostgreSQL y credenciales de todas las integraciones externas listadas en la sección 7.
# Compilar sin tests (patrón estándar aquí)
./mvnw -B -DskipTests clean install
# Compilar con tests
./mvnw clean install
# Ejecutar un test concreto
./mvnw test -Dtest=MyTestClassName
# Análisis SonarQube (usado en CI)
mvn sonar:sonar -Dmaven.test.skip=true -Dsonar.projectKey=<key> -Dsonar.host.url=<url> -Dsonar.login=<token>
En dev/entornos distintos de pro, SecurityConfig permite todas las rutas (permitAll) aunque el filtro JWT sigue configurado. Verificación de que el servicio está operativo: no hay Actuator configurado; se recomienda usar /api/check-domain (público) como comprobación básica de disponibilidad.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), incluyendo además el directoriotemplates/(plantillas de factura/email) como directorio extra en la imagen. Publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/infranete-api:<tag>. - Orquestación: Kubernetes
Deployment(k8s/deployment.yaml, noCronJob) en el clúster GKEpi-cluster-hw(zonaeurope-west3-a, proyectopi-saldum), namespacepi,replicas: 1. - CI/CD (Jenkins): pipeline real de 2 etapas —
Checkout→Build & Push(sustituyeapplication-pro.propertiesporapplication.properties) →Deploy to GKE(aplica el manifiesto templado y esperarollout status). ElCLAUDE.mddescribe un pipeline mucho más extenso (Build → KICS Scan → SonarQube → Test → Docker Push → K8s Deploy → Clean) y menciona una imagen baseeclipse-temurin:17-jdk-alpinecon heap de 5 GB (-Xmx5g) y un manifiesto enjenkins/deployment/app-deployment.yml; ninguno de estos detalles coincide con elJenkinsfile/pom.xml/estructura de directorios actuales (ver hallazgo en la sección 13). - Las variables sensibles se inyectan en el pod mediante un
Secretde Kubernetes llamado igual que la app (infranete-api), con un número muy elevado de claves (5 bases de datos + Auth0 + todas las integraciones de marketplace).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/infranete-api/
12. Manejo de errores y logging
No se ha localizado un @RestControllerAdvice global centralizado en el código revisado; el manejo de errores parece resolverse por controlador/servicio. CleanApiRunner (desactivado) captura excepciones genéricas dentro de su bucle y solo registra un WARNING. Los niveles de log de Jackson/HTTP y Spring Security están elevados a DEBUG en application.properties (adecuado solo para desarrollo; debería revisarse antes de cualquier uso en producción, dado que puede filtrar información sensible en los logs). Notificación de errores a Slack disponible vía slack-client, aunque su uso específico no se ha podido verificar exhaustivamente dado el tamaño del proyecto.
13. Notas y consideraciones
CleanApiRunnercontiene un bucle infinito sin pausa (while(true)sinThread.sleep): aunque está desactivado (@Componentcomentado), el código tal cual quedaría escrito ejecutaríashipmentService.clean()en un bucle infinito y sin ningún retardo entre iteraciones si se reactivara — un busy-loop que consumiría CPU de forma continua y generaría carga constante e innecesaria sobre la base de datoslogistics. Si en algún momento se reactiva esta clase, debe añadirse como mínimo una espera (Thread.sleepo, mejor, convertirlo en un@Scheduled) antes de descomentar el@Component.- Doble configuración de CORS potencialmente conflictiva:
InfraneteApiApplicationregistra unWebMvcConfigurerque permiteallowedOrigins("*")en/**para los métodosGET/POST/PUT/DELETE, mientras queSecurityConfigregistra por separado unCorsFiltercon precedencia máxima que restringe el origen a${infranete.interface}(conallowCredentials(true)). Tener dos configuraciones CORS distintas y potencialmente contradictorias en la misma aplicación dificulta razonar sobre el comportamiento real ante peticiones cross-origin; se recomienda consolidar en una única fuente de verdad. - Advertencia de migración de Feign a
@HttpExchangepresente tanto enCLAUDE.mdcomo en un comentario del propio código (InfraneteApiApplication): ambos documentos afirman que las librerías internas de cliente (shopee-client,slack-client, etc.) siguen usando Spring Cloud OpenFeign y no se pueden inyectar hasta migrarlas a@HttpExchange. Esto contrasta con el patrón ya confirmado en otros proyectos de este mismo ecosistema (p. ej.decathlon-create-db,eci-create-db,donation-update), donde las librerías equivalentes (slack-client,decathlon-client, etc.) en la misma versión1.0.25-SNAPSHOTsí se inyectan correctamente vía@HttpExchange+AutoConfiguration.imports, sin necesidad de@EnableFeignClients. Es posible que esta advertencia esté desactualizada y que la migración ya se haya completado en las librerías compartidas; se recomienda verificarlo directamente (comprobando si los beans deshopee-client/etc. se inyectan sin errores al arrancar) antes de asumir que sigue siendo un bloqueo real. - Discrepancias menores de conteo/estructura en
CLAUDE.md: el documento indica "33 controladores"; el recuento real en el árbol de fuentes es 31. También describe un paquetecontroller/stores/paraapi/sfcc-storesque no existe como tal en la estructura actual (existeservice/logistics/SfccStoreServicepero no se ha localizado un controlador dedicado bajo ese nombre de paquete). InfraneteApiRunnerejecuta lógica de sincronización pesada solo una vez por arranque y solo enpro: al ser una aplicación de larga duración (Deployment, noCronJob), esta ejecución inicial (OriginalFeedsSync,OrderLinesSync,ImagesFeedsSync) solo ocurre en el momento del despliegue/reinicio del pod; el resto del tiempo, la sincronización de estos tres procesos depende exclusivamente de sus jobs@Scheduledcorrespondientes (cuando los tienen —OriginalFeedsSyncno tiene cron activo, solo se dispara al arrancar).- Ver alerta de seguridad de severidad crítica en la sección 7: este es, con diferencia, el proyecto con mayor volumen de credenciales reales expuestas en
application.propertiesde todo este lote de documentación.