# Documentación: Solución del problema CORS entre app.masterbrokervenezuela.com y sistemas.masterbrokervenezuela.com

## 1. Resumen del problema

La aplicación frontend en `https://app.masterbrokervenezuela.com` (React/Vite PWA) no podía hacer peticiones a la API backend en `https://sistemas.masterbrokervenezuela.com` (Laravel 12). El navegador mostraba:

> *"Response to preflight request doesn't pass access control check: It does not have HTTP ok status."*

La respuesta OPTIONS retornaba **503 Service Unavailable** en vez de **204 No Content**.

---

## 2. Causa raíz

### 2.1 Límite de procesos NPROC en CloudLinux LVE

El usuario `masterbr` (UID 1603) tenía un límite de **100 procesos** simultáneos en CloudLinux LVE. Con 10 procesos Node.js de larga duración (PM2, lsnode) más procesos transitorios de cron, el contador alcanzaba picos de 99 procesos.

Cuando LiteSpeed intentaba ejecutar un script PHP:
1. LiteSpeed spawns un proceso `lsphp` (ocupa 1 slot NPROC)
2. `lsphp` intenta hacer `fork()` para crear un worker hijo
3. **fork() falla** con `errno: 11 (Resource temporarily unavailable)` porque se alcanzó el límite NPROC
4. LiteSpeed reintenta 3 veces y luego responde **503 Service Unavailable**

**Error en logs** (`/var/log/apache2/stderr.log`):
```
[UID:1603][3209999] fork() failed, please increase process limit, errno: 11 (Resource temporarily unavailable)
```

Total de fallos de fork registrados: **24,920**

### 2.2 Múltiples handlers PHP para el mismo usuario

El usuario `masterbr` tenía **4 sockets de handler PHP activos** simultáneamente:
- `APVH_masterbr_Suea-php84.sock` — ea-php84 (usado por sistemas)
- `APVH_masterbr_Sualt-php84.sock` — alt-php84
- `APVH_masterbr_Suea-php82.sock` — ea-php82 (handler antiguo)
- `APVH_masterbr_Suphp.sock` — suPHP genérico

Cada handler requería procesos `lsphp` independientes, multiplicando el consumo de NPROC.

### 2.3 Múltiples dominios bajo el mismo usuario

El usuario `masterbr` aloja varios dominios/subdominios:
- `sistemas.masterbrokervenezuela.com` — Laravel (PHP 8.4)
- `fisializ.masterbrokervenezuela.com` — Node.js
- `plataforma.masterbrokervenezuela.com` — Node.js
- `app.masterbrokervenezuela.com` — Frontend (Node.js vía PM2)
- `clubatleticoba` — Node.js

---

## 3. Configuración del servidor

### 3.1 Stack tecnológico

| Componente | Versión |
|---|---|
| OS | CloudLinux 9.8 |
| Servidor web | LiteSpeed 6.3.5 Enterprise |
| PHP | ea-php84 8.4.23 (LSAPI) + alt-php84 8.4.23 |
| WAF | Imunify360 WebShield (OpenResty 1.31.1.1) |
| Limitación | CloudLinux LVE + CageFS |
| Panel | cPanel/Plesk |
| Frontend | Node.js vía PM2 + lsnode |
| Backend | Laravel 12 (PHP 8.4) |

### 3.2 Arquitectura de red

```
Internet (puerto 443)
    │
    ├── IPs en i360.ipv4.remote_proxy (Cloudflare, etc.)
    │   └── NAT: 443 → 127.0.0.1:52223 → WebShield → LiteSpeed
    │
    └── IPs NO en remote_proxy (usuarios directos)
        └── Directo a LiteSpeed en 0.0.0.0:443
```

Puertos relevantes:
- `443/tcp` — LiteSpeed (principal)
- `7080/tcp` — LiteSpeed admin (no accesible externamente)
- `52223/tcp` — WebShield (solo loopback)
- `8130/tcp` — Imunify360 admin

### 3.3 Directorios del proyecto

```
/home2/masterbr/
├── public_html/
│   ├── sistemas.masterbrokervenezuela.com/   ← API Laravel
│   ├── plataforma.masterbrokervenezuela.com/ ← Node.js
│   └── fisializ.masterbrokervenezuela.com/   ← Node.js (ruta alternativa)
├── clubatleticoba/                            ← Node.js (PM2)
└── nodejs/
    └── fisializ/                              ← Node.js (lsnode)
```

---

## 4. Cambios realizados

### 4.1 Headers CORS en WebShield (Protección ante 503)

Se crearon archivos de configuración CORS a nivel global del WebShield para que **cualquier respuesta** (incluso errores 503 de LiteSpeed) incluya los headers CORS necesarios.

**Archivo server-level**: `/etc/imunify360-webshield/webshield-server.conf.d/cors.conf`
```nginx
add_header Access-Control-Allow-Origin "https://app.masterbrokervenezuela.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, Accept, X-Requested-With, X-XSRF-TOKEN" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age "86400" always;
```

**Archivo backend-level**: `/etc/imunify360-webshield/webshield-backend.conf.d/cors.conf`
```nginx
add_header Access-Control-Allow-Origin "https://app.masterbrokervenezuela.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, Accept, X-Requested-With, X-XSRF-TOKEN" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age "86400" always;
```

La directiva `always` asegura que los headers se añadan incluso en respuestas de error (códigos 4xx/5xx).

### 4.2 Handler PHP cambiado a ea-php84

**Archivo**: `/home2/masterbr/public_html/sistemas.masterbrokervenezuela.com/.htaccess`

Se cambió el handler PHP de `ea-php82` (obsoleto) a `ea-php84`:

```apache
AddHandler application/x-httpd-ea-php84___lsphp .php .php8 .phtml
```

El sufijo `___lsphp` es necesario para que LiteSpeed use el conector LSAPI en vez de CGI.

### 4.3 Configuración CORS de Laravel

**Archivo**: `config/cors.php`
```php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['https://app.masterbrokervenezuela.com'],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 86400,
    'supports_credentials' => true,
];
```

Se cachéó la configuración con:
```bash
/opt/cpanel/ea-php84/root/usr/bin/php artisan config:cache
```

### 4.4 Límite NPROC aumentado

**Límite anterior**: 100 procesos
**Nuevo límite**: 200 procesos

Comando ejecutado:
```bash
lvectl set 1603 --nproc=200
```

Verificación:
```bash
lvectl list 1603
```
→ `1603 400 4096M 0K 40 200 2048 1024`

### 4.5 Sesión cambiada a archivo

**Archivo**: `.env`
```
SESSION_DRIVER=file
```

Anteriormente estaba en `database` y no había base de datos configurada.

---

## 5. Comandos de verificación

### 5.1 Probar CORS (OPTIONS preflight)

```bash
# HTTP/1.1
curl -s -D- -X OPTIONS \
  -H "Origin: https://app.masterbrokervenezuela.com" \
  "https://sistemas.masterbrokervenezuela.com/api/login"

# HTTP/2
curl -s -D- --http2 -X OPTIONS \
  -H "Origin: https://app.masterbrokervenezuela.com" \
  "https://sistemas.masterbrokervenezuela.com/api/login"
```

Respuesta esperada: `204 No Content` con headers:
- `access-control-allow-origin: https://app.masterbrokervenezuela.com`
- `access-control-allow-methods: GET, POST, PUT, DELETE, OPTIONS`
- `access-control-allow-credentials: true`

### 5.2 Probar API funcionando

```bash
curl -s -o /dev/null -w "%{http_code}" \
  "https://sistemas.masterbrokervenezuela.com/api/productos"
```

Respuesta esperada: `401` (Unauthorized — la API responde pero requiere autenticación)

### 5.3 Verificar estado LVE

```bash
lvectl list 1603
lveinfo --user masterbr
```

### 5.4 Verificar logs de PHP

```bash
tail -20 /var/log/apache2/stderr.log
tail -20 /var/log/apache2/error_log
```

---

## 6. Lecciones aprendidas

### 6.1 Síntomas de límite NPROC agotado

- PHP retorna **503 Service Unavailable** con mensaje "The server is temporarily busy, try again later!"
- El error aparece en **ambos protocolos** (HTTP/1.1 y HTTP/2) por igual
- Los archivos estáticos (favicon.ico, imágenes) **sirven normalmente** (código 200)
- En `/var/log/apache2/stderr.log` aparecen mensajes: `fork() failed, please increase process limit`
- Los procesos `lsphp` existen pero no tienen el file descriptor 3 (socket de escucha)

### 6.2 Arquitectura WebShield

El WebShield de Imunify360 (OpenResty) actúa como proxy inverso solo para IPs que están en el ipset `i360.ipv4.remote_proxy`. Las IPs que no están en ese conjunto conectan **directamente** a LiteSpeed, sin pasar por el WebShield.

Esto significa que los headers CORS configurados en WebShield **no aplican** para tráfico directo a LiteSpeed. Sin embargo, la configuración server-level de WebShield también protege casos donde el WebShield sí procesa la petición (IPs proxy).

### 6.3 Múltiples PHP handlers

Cuando un usuario tiene múltiples dominios con diferentes versiones de PHP, cada una requiere su propio conjunto de procesos `lsphp`. Esto multiplica el consumo de NPROC. Para entornos con muchos dominios, se recomienda:
1. Unificar versiones de PHP en todos los dominios del mismo usuario
2. Aumentar NPROC proporcionalmente al número de handlers distintos

---

## 7. Archivos relevantes

| Archivo | Propósito |
|---|---|
| `/etc/imunify360-webshield/webshield-server.conf.d/cors.conf` | Headers CORS globales (WebShield) |
| `/etc/imunify360-webshield/webshield-backend.conf.d/cors.conf` | Headers CORS backend (WebShield) |
| `/home2/masterbr/public_html/sistemas.masterbrokervenezuela.com/.htaccess` | Handler PHP 8.4 + rewrite rules |
| `/home2/masterbr/public_html/sistemas.masterbrokervenezuela.com/config/cors.php` | Configuración CORS de Laravel |
| `/home2/masterbr/public_html/sistemas.masterbrokervenezuela.com/.env` | Variables de entorno (SESSION_DRIVER=file) |
| `/var/log/apache2/error_log` | Error log de LiteSpeed |
| `/var/log/apache2/stderr.log` | Stderr de procesos PHP (fork failures aquí) |
| `/usr/local/lsws/extapp-sock/` | Sockets Unix de los handlers PHP |
| `/etc/apache2/conf.d/php.conf` | Mapeo de handlers PHP a versiones |
| `/etc/apache2/conf.d/lsapi.conf` | Configuración del módulo LSAPI |
