Appearance
Configuracion y Deployment - Portal de Clientes
DOCUMENTACION RETROSPECTIVA - Generada a partir de codigo implementado el 2026-04-27.
Modulo: Portal de Clientes Tipo: Process Estado: Implementado Fecha: 2026-04-27 Repo frontend: portal-usuarios/Repo backend: bautista-backend/
Resumen del modelo de deployment
- Frontend: una unica imagen Docker (
portal-usuarios) que se build-ea N veces (una por tenant) inyectando variablesVITE_*como build args. Cada tenant obtiene su contenedor con su URL de backend, su tenant_id, su sucursal_id y su branding. - Backend: instancia compartida de
bautista-backend, configurada conPORTAL_ALLOWED_ORIGINpara autorizar CORS desde el portal. - Resolucion multi-tenant: NO hay resolucion DNS por dominio. El frontend declara su
tenant_idysucursal_iden build time; el backend resuelve la conexion viaini.sistema.
Variables de entorno - Frontend (portal-usuarios)
Definidas en portal-usuarios/.env.example. Todas son leidas en build time por Vite (import.meta.env.VITE_*) y quedan inlineadas en el bundle estatico.
| Variable | Requerida | Descripcion | Ejemplo |
|---|---|---|---|
VITE_BACKEND_URL | si | URL base de la API backend, sin trailing slash. Usada como baseURL de axios y para CORS. | https://api.tenant.com |
VITE_TENANT_ID | si | ID numerico del tenant (empresa). Se manda en cada request via header X-Tenant-Id. | 1 |
VITE_SUCURSAL_ID | si | ID numerico de la sucursal. Se manda via header X-Sucursal-Id y se usa como sucursalId por defecto en login. | 1 |
VITE_APP_NAME | no | Nombre de la app para branding (titulo del documento, header). | Portal de Clientes |
VITE_LOGO_URL | no | URL absoluta al logo del tenant. | https://cdn.tenant.com/logo.png |
VITE_PRIMARY_COLOR | no | Color primario CSS (hex o hsl). Inyectado como --primary en :root. | #1e40af |
VITE_SECONDARY_COLOR | no | Color secundario CSS. Inyectado como --secondary en :root. | #3b82f6 |
VITE_THEME_COLOR | no | Color de la meta tag theme-color para PWA / barra de status mobile. | #1e40af |
Notas:
- Validacion de variables obligatorias: el
Dockerfilefalla el build siVITE_BACKEND_URL,VITE_TENANT_IDoVITE_SUCURSAL_IDno estan definidas. - Variables opcionales tienen defaults definidos en
BrandingContext.tsx(Portal de Clientes,#1e40af,#3b82f6). - Como Vite inlinea las variables en build time, cada tenant requiere un build distinto. No es posible cambiar
VITE_BACKEND_URLen runtime.
Variables de entorno - Backend (bautista-backend)
Solo las variables del backend que afectan al portal. Definidas en bautista-backend/.env.dist.
| Variable | Requerida | Descripcion | Ejemplo |
|---|---|---|---|
PORTAL_ALLOWED_ORIGIN | si (cuando portal esta activo) | Origen autorizado para CORS desde el portal de clientes. Debe coincidir EXACTO con el origen del frontend (sin barra al final). Usado por PortalCorsMiddleware. | https://portal.tenant.com |
Constantes relacionadas en constants.dist.php:
| Constante | Uso |
|---|---|
ALLOWED_ORIGINS | Origenes permitidos para el ERP principal. NO se usa para el portal -- el portal tiene su propio middleware con PORTAL_ALLOWED_ORIGIN. |
HOST, PORT, USER, PASSWORD, DB_INI | Conexion a la DB principal (ini.sistema) que resuelve la conexion por tenant. |
Notas:
PORTAL_ALLOWED_ORIGINadmite un solo origen. Para soportar multiples tenants en un mismo backend, el valor debe ser el origen de cada portal por separado o el middleware debe reescribirse para aceptar una lista. Pendiente de validacion con stakeholders si el modelo soporta backend compartido entre multiples portales con dominios distintos.- El backend usa JWT con claves RSA. Las claves estan fuera del scope del portal (son globales del backend). Ver
bautista-backend/CLAUDE.mdparaprivate-key.pem/public-key.pem.
data_config - Configuracion en base de datos
Nota: la documentacion existente en
index.mddel modulo menciona que el backend resuelvetenant_id -> DBviaini.sistema. El operador del ERP configura los gateways de pago y otros parametros del portal desde las pantallas de configuracion del ERP.
La estructura concreta de las claves portal.* en la tabla de configuracion (gateway, caja_id, etc.) no fue verificada en el codigo durante esta documentacion retrospectiva. Para evitar inventar contratos, se deja documentada la existencia del mecanismo y se marca como pendiente.
Pendiente de validacion:
- Listado completo de claves
portal.*endata_config(tipos, valores admitidos, defaults). - Mecanismo exacto por el cual el backend resuelve
tenant_iddesdeini.sistema. - Referencias cruzadas a las pantallas de configuracion del ERP donde se setean estas claves.
Una vez validado, este apartado deberia documentarse en una doc tecnica backend dedicada al subsistema de configuracion del portal.
Docker - Frontend
Construccion de la imagen
Definida en portal-usuarios/Dockerfile. Build multi-stage:
| Stage | Base | Proposito |
|---|---|---|
builder | node:22-alpine | npm ci, copiar fuentes, validar build args, ejecutar npm run build (genera /app/dist). |
runner | nginx:alpine | Copia /app/dist a /usr/share/nginx/html, copia nginx.conf, expone puerto 80. |
Build args declarados (todos VITE_*): VITE_BACKEND_URL, VITE_TENANT_ID, VITE_SUCURSAL_ID, VITE_APP_NAME, VITE_LOGO_URL, VITE_PRIMARY_COLOR, VITE_SECONDARY_COLOR, VITE_THEME_COLOR.
Validaciones de build (fallan el build si falta el arg):
VITE_BACKEND_URLVITE_TENANT_IDVITE_SUCURSAL_ID
SPA routing en nginx
Definido en portal-usuarios/nginx.conf:
| Regla | Comportamiento |
|---|---|
location / con try_files $uri $uri/ /index.html | Fallback a index.html para rutas SPA. Permite que TanStack Router maneje rutas como /dashboard, /deudas, etc. sin que nginx devuelva 404. |
location ~* \.(js|css|woff2|png|svg|ico|jpg|webp)$ | Cache-Control: public, immutable; expires 1y. Los assets de Vite tienen hash en el nombre, por eso se pueden cachear inmutablemente. |
location = /index.html | Cache-Control: no-cache, no-store, must-revalidate; Pragma: no-cache; Expires: 0. El entry point NUNCA se cachea, asi cada deploy entra inmediatamente. |
Bundles definidos como manualChunks en vite.config.ts (separan vendor y features para cache eficiente):
vendor-react,vendor-query,vendor-routerfeature-auth,feature-deudas,feature-pagos,feature-cupones
Docker - Deployment por tenant
Modelo
Una sola imagen base, N contenedores (uno por tenant). Cada contenedor se construye con sus propios build args.
| Tenant | Build args distintos | Resultado |
|---|---|---|
| Tenant A | VITE_BACKEND_URL=https://api.bautista.com, VITE_TENANT_ID=1, VITE_SUCURSAL_ID=1, branding A | Imagen portal-tenant-a:latest |
| Tenant B | VITE_BACKEND_URL=https://api.bautista.com, VITE_TENANT_ID=2, VITE_SUCURSAL_ID=3, branding B | Imagen portal-tenant-b:latest |
Implicancias
- Cada tenant requiere su propio build (los
VITE_*son inlineados por Vite). - Una sola imagen no puede servir multiples tenants -- la
VITE_BACKEND_URL,VITE_TENANT_IDyVITE_SUCURSAL_IDquedan fijas en build. - El backend (
bautista-backend) se comparte. Cada portal apunta al mismo backend con sus headersX-Tenant-Id/X-Sucursal-Id.
Resolucion multi-tenant en el flujo de request
Documentada en index.md del modulo. Resumen:
- El frontend manda
X-Tenant-Id,X-Sucursal-Idy (cuando esta autenticado)Authorization: Bearer {access_token}. - El backend resuelve
tenant_id -> base de datosviaini.sistema. - El backend resuelve
sucursal_id -> schema PostgreSQL(suc0001, suc0002, etc.). - El JWT lleva
tenant_idysucursal_id-- el backend valida que coincidan con los headers (proteccion contra tampering).
Compose por tenant
Pendiente de validacion: el repo portal-usuarios no tiene docker-compose.yml ni docker-compose.dev.yml en el momento de esta documentacion retrospectiva. La orquestacion de N contenedores (uno por tenant) probablemente se hace fuera del repo (en un orquestador de infra superior). Validar con DevOps como se gestiona el deploy de tenants nuevos.
Setup local de desarrollo
Pasos minimos para levantar el portal localmente, derivados de package.json y los archivos de config:
| Paso | Comando / accion | Resultado |
|---|---|---|
| 1 | Clonar portal-usuarios y entrar al directorio | -- |
| 2 | Copiar .env.example -> .env.local y completar al menos VITE_BACKEND_URL, VITE_TENANT_ID, VITE_SUCURSAL_ID | Variables Vite disponibles |
| 3 | npm ci | Instala dependencias declaradas en package-lock.json |
| 4 | Levantar backend en paralelo (bautista-backend) con PORTAL_ALLOWED_ORIGIN=http://localhost:5173 (puerto default de Vite) | CORS habilitado para el frontend local |
| 5 | npm run dev | Vite dev server en http://localhost:5173 |
Otros scripts utiles definidos en package.json:
| Script | Comando | Uso |
|---|---|---|
build | tsc -b && vite build | Build de produccion |
preview | vite preview | Servir el build localmente |
test | vitest run --coverage | Tests unitarios + cobertura |
test:watch | vitest | Tests en watch mode |
test:e2e | playwright test | Tests E2E |
lint | eslint src --max-warnings 0 | Lint estricto |
format | prettier --write src tests | Format |
type-check | tsc --noEmit | Type check sin emitir |
Notas:
- Para que la cookie
portal_refresh_tokenfuncione en local, el backend tiene que servirse con HTTPS (Secure) o el atributoSecuredebe relajarse para desarrollo. Actualmente la cookie es siempreSecure; SameSite=None(verPortalAuthController::setRefreshCookie). Pendiente de validacion: como se maneja el setup local con HTTP. Posibles caminos:mkcertpara certificados locales,localhostexempt en algunos browsers, o un override de la cookie en entorno dev. - Las variables
VITE_*opcionales de branding pueden quedar vacias en local;BrandingContexttiene defaults sensatos.
Notas y pendientes
- Listado de claves
portal.*endata_config: no verificado en codigo. Documentar en doc tecnica backend dedicada cuando se valide. docker-compose.yml: no presente en el repo. Validar con DevOps el orquestador real.- HTTPS en local: la cookie
Secure; SameSite=Noneno funciona sobre HTTP. Falta documentar el flujo recomendado para desarrollo local. - Multi-portal con backend compartido:
PORTAL_ALLOWED_ORIGINes un unico string. Si hay varios portales con dominios distintos contra el mismo backend, hay que validar como se autorizan todos (o reescribir el middleware para aceptar lista).
Ver tambien
- Autenticacion Frontend - Como se usan
VITE_BACKEND_URL,VITE_TENANT_ID,VITE_SUCURSAL_IDen el flujo de auth. portal-usuarios/Dockerfile- Build multi-stage con argsVITE_*.portal-usuarios/nginx.conf- SPA routing y politicas de cache.portal-usuarios/.env.example- Template de variables del frontend.portal-usuarios/vite.config.ts- Manual chunks y aliases.bautista-backend/.env.dist-PORTAL_ALLOWED_ORIGINy demas config backend.bautista-backend/Modules/Portal/Infrastructure/Http/Middleware/PortalCorsMiddleware.php- Middleware que consumePORTAL_ALLOWED_ORIGIN.- Multi-Tenancy del portal - Como se resuelve
tenant_id-> DB ysucursal_id-> schema. - Infraestructura del deployment - (si existe) Diagrama de la infra Docker por tenant.
NOTA IMPORTANTE: Documentacion retrospectiva. Validar con stakeholders antes de considerarla final. Las secciones marcadas como "Pendiente de validacion" requieren confirmacion del equipo de DevOps / backend.