# Portal de reportes con Virtualmin

Virtualmin es un panel parecido a cPanel que se instala en un VPS tuyo. Es
viable para este portal, pero hay que cambiar **dos** límites de tiempo, no uno.

---

## 1. ¿Virtualmin o el kit de `DEPLOY-VPS.md`?

| | Virtualmin | Kit de `DEPLOY-VPS.md` |
|---|---|---|
| Panel | Sí, tipo cPanel | No, por SSH |
| Varios sitios en un mismo VPS | Fácil | Hay que hacerlo a mano |
| Correo, DNS, cuotas, usuarios | Incluidos | Nada |
| Capas entre tú y el problema | Virtualmin + nginx + PHP-FPM + Laravel | nginx + PHP-FPM + Laravel |
| Riesgo de que se sobrescriba tu configuración | **Sí**: Virtualmin regenera el nginx al modificar un sitio | No |
| Tiempo para el primer despliegue | Más largo | Más corto |

**Mi recomendación:** si vas a tener varios portales o sitios en el mismo
servidor, usa Virtualmin. Si este es el único y quieres la menor cantidad de
capas posibles, el kit de `DEPLOY-VPS.md` es más directo.

Si ya instalaste Virtualmin o lo vas a instalar, sigue esta guía.

---

## 2. Instalar Virtualmin

Sobre un VPS **nuevo** con Ubuntu 24.04 o Debian 12, Virtualmin se instala
desde un script. Antes de correrlo, ten el servidor recién hecho: el script
cambia los repositorios, el firewall y los servicios, y a medio camino es
difícil saber qué tocó.

```bash
# Descarga
curl -L -O http://download.virtualmin.com/gpl/virtualmin-latest.sh
chmod +x virtualmin-latest.sh

# Ojo: primero en modo de prueba
./virtualmin-latest.sh --mode bootstrap
```

La secuencia normal es:

```bash
./virtualmin-latest.sh --mode bootstrap   # descarga y prepara
# BorgBackup  -> n  (no lo necesitamos)
# Reemplazar el script  -> s  (deja el archivo actualizado)

# Instalacion
./virtualmin-latest.sh --mode install

# Dependencias (pregunta por paquetes):
#   Desinstalar webmin  -> n
#   Instalar con o sin Virtualmin  -> s
#   Cambiar puerto de Webmin  -> n  (deja 10000)
#   SSL -> y (Let's Encrypt)
```

Al terminar, Webmin queda en `https://TU_IP:10000` con usuario `root` y la
contraseña que generó (está en `/root/virtualmin-install.log`).

---

## 3. Crear el dominio

En el panel:

1. **Create a Virtual Server** (en Webmin: *Servers* → *Add New Server*)
2. Domain name: `reportes.incide.com.co`
3. Password: la del usuario del sitio
4. Administration user: **No**, marca *Administration user* → No

Ojo con esto: **no actives "Administration user"**. Si lo activas, el usuario del
sitio se vuelve administrador y se mete en el panel. Para un portal de reportes
no hace falta.

5. En **Features and Plugins**, activa:
   - ✅ **PHP**
   - ✅ **SSL**
   - ❌ **DNS**
   - ❌ **Mail**
   - ❌ **FTP** (no lo vas a usar)

6. En **PHP Options**, elige:
   - Execution mode: **PHP-FPM**
   - PHP version: **8.3**

> Con Apache activado, `mod_php` no aísla sitios y Virtualmin lo desaconseja
> explícitamente. Si activaste Apache por error, quítalo antes de seguir.

---

## 4. Los dos límites de tiempo: lo importante

Un reporte que no está en caché tarda **69,8 segundos**, porque tiene que
descargar las imágenes del servidor de Sihos. Los valores por defecto cortan la
petición antes:

| Límite | Por defecto | Necesario | Dónde |
|---|---|---|---|
| `max_execution_time` | 30 s | **300** | PHP del dominio |
| `max_input_time` | 60 s | **300** | PHP del dominio |
| `fastcgi_read_timeout` | 60 s | **300** | **nginx** |

Si solo cambias el de PHP, el portal va a fallar igual: PHP sigue trabajando,
pero nginx corta la conexión y el navegador muestra un error. **Y el PDF sí se
generó**, lo cual hace el fallo muy confuso.

### 4.1 Límites de PHP

En el panel, dentro del dominio: **Web Configuration → PHP Configuration → Edit**

```
max_execution_time     300
max_input_time         300
memory_limit           512M
post_max_size          16M
upload_max_filesize    16M
```

O por consola, más rápido:

```bash
virtualmin modify-php-ini --domain reportes.incide.com.co \
  --ini-name max_execution_time --ini-value 300

virtualmin modify-php-ini --domain reportes.incide.com.co \
  --ini-name max_input_time --ini-value 300

virtualmin modify-php-ini --domain reportes.incide.com.co \
  --ini-name memory_limit --ini-value 512M

virtualmin modify-php-ini --domain reportes.incide.com.co \
  --ini-name post_max_size --ini-value 16M

virtualmin modify-php-ini --domain reportes.incide.com.co \
  --ini-name upload_max_filesize --ini-value 16M
```

### 4.2 Límite de nginx

Este es el que se olvida. En Webmin:

**System Settings → Server Templates → Default settings** → campo
**Nginx config directives**:

```
fastcgi_read_timeout 300s; fastcgi_send_timeout 300s;
```

Eso aplica a los sitios que crees después. Para el que ya creaste, revisa el
archivo que generó Virtualmin:

```bash
ls /etc/nginx/conf.d/
nano /etc/nginx/conf.d/reportes.incide.com.co.conf
```

Ahí busca el bloque `location` que apunta al socket de PHP-FPM y añade dentro:

```
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_param HTTPS on;
```

> **Advertencia:** Virtualmin regenera ese archivo cada vez que modificas algo
> del sitio. Si lo editas a mano, el cambio se pierde. Por eso conviene ponerlo
> en **Server Templates**, no en el archivo. Si aun así lo editas, anótalo:
> la próxima vez que toques la configuración del dominio hay que volver a
> ponerlo.

> `fastcgi_param HTTPS on;` también es importante: tu `TrustProxies` de Laravel
> no confía en ningún proxy, así que sin esto PHP cree que la petición es `http`
> y las URLs que genera el portal salen sin cifrar.

### 4.3 Comprobar que quedó

```bash
# PHP
virtualmin modify-php-ini --domain reportes.incide.com.co --show-all \
  | grep -E "max_execution_time|memory_limit"

# nginx: el valor debe aparecer en la configuración cargada
nginx -T 2>/dev/null | grep -A3 "fastcgi_read_timeout" | head -20
```

---

## 5. Raíz del documento

Dentro del dominio: **Web Configuration → Document root**

Debe ser:

```
/home/USUARIO/public        ✅
/home/USUARIO                ❌ nunca
```

Si queda apuntando a la carpeta del proyecto, quedan expuestos el `.env` (con
las credenciales de Sihos) y la base SQLite con los enlaces de descarga.

---

## 6. Subir el proyecto

Desde tu PC:

```powershell
php tools\armar-paquete.php
scp incide-deploy.zip USUARIO@TU_IP:/home/USUARIO/
```

Y en el servidor, **como el usuario del sitio** (no como root):

```bash
cd /home/USUARIO
unzip -q ~/incide-deploy.zip
rm ~/incide-deploy.zip
bash deploy/virtualmin/preparar-dominio.sh /home/USUARIO
```

El script se encarga de: crear la estructura de carpetas, instalar `vendor`,
poner permisos, crear el `.env` desde la plantilla y correr las comprobaciones.

Al final imprime los pasos que faltan, que son los del punto 4.

---

## 7. El `.env`

```bash
cd /home/USUARIO
nano .env
```

```ini
APP_ENV=production
APP_DEBUG=false
APP_URL=https://reportes.incide.com.co

DB_CONNECTION=mysql
DB_HOST=incide.sihos.com.co     <- el hostname, NO la IP
DB_PORT=3306
DB_DATABASE=sihos
DB_USERNAME=incidebd
DB_PASSWORD=la_que_te_dio_sihos

SQLITE_DATABASE=database/sqlite/app_data.sqlite

SESSION_DOMAIN=null
SANCTUM_STATEFUL_DOMAINS=reportes.incide.com.co
SESSION_SECURE_COOKIE=true

SIHOS_URL=http://incide.sihos.com.co/sihos
SIHOS_URL_IP=http://incide.sihos.com.co
SIHOS_IP=186.115.199.25
SIHOS_USER=incidebd
SIHOS_PASSWORD=la_que_te_dio_sihos
SIHOS_DATABASE=sihos
SIHOS_USER_WEB=datalab
SIHOS_PASSWORD_WEB=la_que_te_dio_sihos
SIHOS_ENTIDAD=410010039801
SIHOS_PDF_AMOUNT=5
```

```bash
php artisan key:generate --show    # pégalo en APP_KEY
php artisan instalacion:verificar --fix
```

---

## 8. Certificado SSL

En el panel, dentro del dominio: **SSL Certificate → Request Certificate**
→ **Let's Encrypt**. Es gratis y se renueva solo.

O por consola:

```bash
virtualmin generate-letsencrypt-cert --domain reportes.incide.com.co \
  --host reports.incide.com.co --webroot yes
```

---

## 9. Verificar

```bash
cd /home/USUARIO
php artisan instalacion:verificar
```

Esperado:

```
  ACCESO A SIHOS
   OK MySQL                  5.6.51-91.0
   OK Pacientes legibles     60.527
   OK PDF de prueba          999,0 KB (admisión 202610020003)
```

### Diagnóstico

| Síntoma | Causa probable |
|---|---|
| `500` en todo | Raíz del documento apuntando fuera de `public` |
| `403 Forbidden` | Permisos de `storage` y `bootstrap/cache` |
| `The /storage/framework/views directory must be present` | Falta correr `preparar-dominio.sh` |
| `Access denied for user 'incidebd'` | `DB_USERNAME` o `DB_PASSWORD` mal escritos |
| **Error al abrir un reporte, pero el PDF sí se generó** | **Falta el `fastcgi_read_timeout` de nginx (punto 4.2)** |
| URLs del portal salen con `http://` en vez de `https://` | Falta `fastcgi_param HTTPS on;` |

---

## 10. Actualizar después

```powershell
.\deploy\local\subir.ps1 -Servidor USUARIO@TU_IP
```

Igual que en el kit de `DEPLOY-VPS.md`: sube solo lo que cambió, nunca el
`.env` ni `storage/app`.

---

## 11. Lo que hay que vigilar

**Virtualmin regenera la configuración de nginx.** Si editas
`/etc/nginx/conf.d/reportes.incide.com.co.conf` a mano y después cambias algo del
sitio en el panel, el `fastcgi_read_timeout` desaparece y los reportes empiezan a
fallar sin que cambies una línea del portal. Si eso pasa, pon el valor en
**Server Templates** y aplica.

**Copia de seguridad.** Lo único que no se regenera solo es la base SQLite con
los enlaces ya emitidos:

```bash
cp /home/USUARIO/database/sqlite/app_data.sqlite ~/app_data-$(date +%F).sqlite
```

---

## Antes de publicarlo

1. **La contraseña es el número de documento.** Cualquiera que lo conozca entra
   a los reportes clínicos. Con el portal en internet esto es urgente.
2. **`public/test.html` no tiene contraseña.** Bórrala cuando el portal real
   esté listo:

   ```bash
   rm /home/USUARIO/public/test.html
   ```