Configuración y despliegue
1. Variables de entorno
Todas son de servidor (ninguna con prefijo NEXT_PUBLIC_). En local se definen en .env.local (ignorado por git); en el clúster proceden del Secret de Kubernetes customer-back-office.
Auth0 (sesión de empleado)
| Variable | Descripción | Valor de referencia |
|---|---|---|
AUTH0_SECRET | Secreto de cifrado de la cookie de sesión del SDK | secreto |
APP_BASE_URL | URL base de la app; el SDK la usa para el callback y los redirects | http://localhost en local |
AUTH0_DOMAIN | Tenant de Auth0 | https://robo-hawkers.eu.auth0.com |
AUTH0_ISSUER_BASE_URL | Issuer del tenant | igual que el dominio |
AUTH0_CLIENT_ID | Client ID de la aplicación | secreto |
AUTH0_CLIENT_SECRET | Client secret | secreto |
AUTH0_AUDIENCE | Audience del API — permite usar el access token contra robo-api | https://robo-api.hawkersco.net |
AUTH0_SCOPE | Scopes solicitados. Debe pasarse explícitamente en el SDK v4 | openid profile email admin atc customer store |
robo-api
| Variable | Descripción | Valor de referencia |
|---|---|---|
ROBO_API_URL | URL base de robo-api | https://robo-api.hawkersco.net |
Marketing Cloud
| Variable | Descripción |
|---|---|
MC_AUTH_URL | Endpoint /v2/token del tenant |
MC_API_URL | Endpoint REST base del tenant |
MC_CLIENT_ID | Client ID del paquete instalado |
MC_CLIENT_SECRET | Client secret |
MC_EVENT_KEY_CREATED | EventDefinitionKey del journey de creación |
MC_EVENT_KEY_APPROVED | EventDefinitionKey del journey de aprobación |
MC_EVENT_KEY_REFUNDED | EventDefinitionKey del journey de reembolso (sin uso hoy) |
PDF y pruebas
| Variable | Descripción |
|---|---|
PDF_SIGNATURE_SECRET | Secreto HMAC de las URLs de PDF. Debe ser idéntico al del portal del cliente |
TEST_EMAIL | Email que reemplaza al del cliente en los journeys cuando NODE_ENV !== "production" |
API_TEST_HEADER | Cabecera de modo de pruebas de robo-api — X-Test-Mode |
API_TEST_HEADER_VALUE | Valor de esa cabecera (true). Solo se envía si NODE_ENV !== "production" |
En total, 19 variables. No hay .env.example en el repositorio.
2. Ejecución en local
Requisitos: Node 22 (.nvmrc) y npm.
nvm use
npm install
npm run dev # next dev --turbopack -p 80 → http://localhost
- El puerto es el 80 en
devy enstart, coherente con la imagen y el manifiesto de Kubernetes.APP_BASE_URL=http://localhost(sin puerto) debe coincidir con eso, o el callback de Auth0 fallará. - La URL de callback (
http://localhost/auth/callback) tiene que estar dada de alta en la aplicación de Auth0. - Se necesita un
.env.localcon las ochoAUTH0_*,ROBO_API_URLyPDF_SIGNATURE_SECRETcomo mínimo. Sin lasMC_*, todo funciona salvo el envío de emails. - Con
API_TEST_HEADER=X-Test-ModeyAPI_TEST_HEADER_VALUE=true, las escrituras van a las tablas sombra de robo-api: es el modo recomendado para desarrollar. - El usuario con el que se entre debe tener rol
ADMINen Auth0 para ver todas las secciones; con otro rol solo se verá Devoluciones. .vscode/launch.jsonincluye una configuración Debug Next.js que arrancanpm run devconNODE_OPTIONS=--inspecten el puerto 9229.
El README.md del repositorio es el generado por create-next-app y no se ha actualizado: menciona http://localhost:3000, que no es el puerto de este proyecto.
Otros comandos: npm run build, npm start, npm run lint.
3. Imagen Docker
Dockerfile multi-stage, ambas etapas sobre node:22-alpine (coincide con .nvmrc) y con libc6-compat instalado en las dos:
- builder:
npm cache clean --force && npm ci --legacy-peer-depsynpm run build. - runner: copia
public,.next,node_modulesypackage.json, crea el usuario no-rootappuser:appgroup, hacechown -Rsobre/app, cambia aUSER appuser,EXPOSE 80y arranca connpm start.
--legacy-peer-deps es necesario por conflictos de peer dependencies del árbol actual; quitarlo requiere resolver esos conflictos primero.
4. Pipeline de Jenkins
Jenkinsfile declarativo con tres etapas, idéntico en estructura al del portal del cliente:
| Etapa | Acción |
|---|---|
| Checkout | checkout scm |
| Build & Push | gcloud builds submit --tag <REGISTRY>/customer-back-office:<BUILD_NUMBER> (Cloud Build → Artifact Registry) |
| Deploy | Credenciales del clúster, borrado del deployment anterior, sed del tag en k8s/deployment.yaml, kubectl apply y kubectl rollout status |
| Variable | Valor |
|---|---|
GCP_PROJECT | pi-saldum |
GKE_CLUSTER | pi-cluster-hw |
GKE_ZONE | europe-west3-a |
REGISTRY | europe-west3-docker.pkg.dev/pi-saldum/pi-repo |
IMAGE_NAME | customer-back-office |
GKE_NAMESPACE | pi |
Ojo con el nombre: la imagen, el deployment y el Secret se llaman customer-back-office, no customer-back-office-returns-portal como el repositorio.
El deploy borra el Deployment y espera a que mueran los pods antes de reaplicarlo, así que hay corte de servicio de unos segundos en cada release.
Una revisión anterior del pipeline incluía análisis con SonarQube; se eliminó (commit 2846e89).
5. Kubernetes (k8s/deployment.yaml)
| Propiedad | Valor |
|---|---|
| Namespace | pi |
| Réplicas | 1 |
| Estrategia | Recreate |
| Imagen | …/pi-repo/customer-back-office:latest (placeholder; sed lo sustituye) |
imagePullPolicy | IfNotPresent |
containerPort | 80 (coincide con el puerto real de la app) |
| Configuración | envFrom.secretRef: customer-back-office |
automountServiceAccountToken | false |
| Requests | 256 Mi memoria / 100m CPU / 256 Mi ephemeral-storage |
| Limits | 512 Mi memoria / 500m CPU / 512 Mi ephemeral-storage |
El repositorio solo contiene el Deployment: el Service y el Ingress se gestionan fuera.
6. Manejo de errores y logging
- Las funciones de
actions/lanzanError(DEFAULT_ERROR_MESSAGE)— un mensaje genérico en español del diccionario — cuando la respuesta de robo-api no es OK. El detalle del error de la API no llega al usuario. - Los route handlers devuelven
500con"Failed to fetch data"y hacenconsole.log(error). - En cliente,
fetchWrapper(enservices/api.ts) centraliza el tratamiento: los401conmissing_session/missing_refresh_tokenredirigen a login/logout, y cualquier otro fallo se convierte enDEFAULT_ERROR_MESSAGEmostrado como toast desonner. - Cada ruta del dashboard tiene su
loading.tsx, pero loserror.tsxsolo existen en cuatro segmentos —returns,methods,methods/[id]yreasons/[id]—, así que en el resto (returns/[id],reasons,users,warehouses) un error de carga sube al error boundary global de Next.js. - No hay integración con Sentry ni logging estructurado: solo
console.log/console.error, visibles en los logs del pod.