# Portal de reportes en un VPS

Guía para publicar el portal en un VPS con Ubuntu, en lugar del hosting
compartido de Banahosting.

## Por qué un VPS y no el plan compartido

Un reporte que no está en caché tarda **69,8 segundos** en armarse, porque tiene
que descargar las imágenes del servidor de Sihos. La segunda vez que se pide el
mismo reporte, tarda **0,48 segundos**.

En el hosting compartido, `max_execution_time` viene en 30 segundos y hay unos
pocos workers de PHP. Un reporte en frío los ocupa durante más de un minuto, y
si dos pacientes piden uno al tiempo, el segundo se queda esperando sin poder
servir nada. En el VPS tú pones ese límite en 300 segundos y los workers no se
agotan.

---

## 1. Contratar el VPS

Cualquiera de estos sirve. Todos tienen el mismo panel y los mismo pasos:

| Proveedor | Plan | Precio aprox. |
|---|---|---|
| DigitalOcean | Droplet 1 GB | USD 6/mes |
| Vultr | Cloud Compute 1 GB | USD 6/mes |
| Linode (Akamai) | Nanode 1 GB | USD 5/mes |
| Hetzner | CX22 | EUR 4/mes, el más barato |

Al crearlo pide:

- **Imagen:** Ubuntu 24.04 LTS (la que dice "24.04 LTS x64")
- **Región:** la más cercana a Colombia. Bogotá si existe; si no, São Paulo o Virginia
- **Autenticación:** llave SSH. La genera el panel y te la descarga

Alta necesitas el archivo `.pem` que te dio el proveedor y la IP del servidor.

---

## 2. Entrar al servidor

Desde Windows, PowerShell:

```powershell
ssh root@TU_IP_VPS
```

La primera vez te pregunta si confía en la llave. Escribe `yes`.

La llave `.pem` debe estar en la carpeta desde donde corres el comando, o en
`C:\Users\TU_USUARIO\.ssh\`.

Si da error de permisos en Windows:

```powershell
icacls TU_LLAVE.pem /inheritance:r
icacls TU_LLAVE.pem /grant:r "$env:USERNAME:(R)"
```

---

## 3. Subir el proyecto y ejecutar el instalador

**Opción A — desde tu PC** (recomendada, un solo comando):

```powershell
.\deploy\local\subir.ps1 -Servidor root@TU_IP_VPS -Todo
```

**Opción B — a mano**, si prefieres paso a paso:

Desde tu PC, comprimes el proyecto y lo subes:

```powershell
php tools\armar-paquete.php
scp incide-deploy.zip root@TU_IP_VPS:/tmp/
```

Y en el servidor:

```bash
apt-get install -y unzip
unzip -q /tmp/incide-deploy.zip -d /var/www/incide
cd /var/www/incide
chmod +x deploy/servidor/*.sh
sudo bash deploy/servidor/install.sh
```

El instalador deja listo: PHP 8.3 con sus extensiones, nginx, Composer, 2 GB de
swap, firewall, fail2ban y el usuario `incide`.

---

## 4. Apuntar el dominio al VPS

En el panel donde están los DNS de `incide.com.co` (puede ser Banahosting, GoDaddy
u otro), crea un registro:

```
Tipo:     A
Nombre:   reportes
Valor:    TU_IP_VPS
TTL:      3600
```

Queda `reportes.incide.com.co → TU_IP_VPS`.

**El sitio principal `incide.com.co` no se toca**: sigue en Banahosting con su
WordPress. Solo el portal de reportes se mueve al VPS.

Espera a que propague (unos minutos). Comprueba:

```powershell
nslookup reportes.incide.com.co
```

Cuando muestre tu IP, sigue.

---

## 5. Crear el `.env` en el servidor

El `.env` **nunca viaja en el ZIP**: lleva las credenciales reales de Sihos y la
ruta de la base local. Se crea a mano.

```bash
cd /var/www/incide
cp .env.example .env
nano .env
```

Lo esencial:

```ini
APP_ENV=production
APP_DEBUG=false
APP_URL=https://reportes.incide.com.co
APP_KEY=                        <- se genera en el paso siguiente

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
```

Guarda con `Ctrl+O`, `Enter`, `Ctrl+X`.

---

## 6. Permisos y llave de cifrado

```bash
cd /var/www/incide

# La llave de cifrado: también hay que anotarla en el .env
php artisan key:generate --show
```

Copia el valor que aparece (empieza con `base64:`) y pégalo en `APP_KEY`.

```bash
nano .env      # pega la llave, Ctrl+O Enter Ctrl+X

# Permisos: PHP corre como www-data y necesita escribir
chown -R incide:www-data storage bootstrap/cache database/sqlite
chmod -R 775 storage bootstrap/cache database/sqlite

# Crea carpetas, tablas y limpia caché
php artisan instalacion:verificar --fix
```

---

## 7. Activar HTTPS

```bash
apt-get install -y certbot python3-certbot-nginx

certbot certonly --webroot -w /var/www/html -d reportes.incide.com.co -m TU@CORREO
```

Cuando pregunte si comparte el correo con Let's Encrypt, responde `N`.

Luego activa la configuración completa:

```bash
cd /var/www/incide
bash deploy/servidor/activar-https.sh reportes.incide.com.co
```

Ahora sí en `https://reportes.incide.com.co`.

---

## 8. Verificar

```bash
cd /var/www/incide
php artisan instalacion:verificar
```

Lo esperado:

```
  ENTORNO
   OK APP_URL                 https://reportes.incide.com.co
   OK SQLITE_DATABASE         database/sqlite/app_data.sqlite

  PERMISOS DE ESCRITURA
   OK carpetas comprobadas    12

  BASE DE DATOS LOCAL (SQLite)
   OK DownloadsHojaProc       0 fila(s)
   OK UrlSignature            0 fila(s)

  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)
```

Si dice **"Todo correcto"**, funciona.

### Si algo falla

| Mensaje | Solución |
|---|---|
| `Access denied` al escribir | `chown -R incide:www-data storage bootstrap/cache database/sqlite` |
| `Connection refused` o `timed out` en MySQL | Revisa `DB_HOST`: debe ser el hostname, no la IP. Prueba `telnet incide.sihos.com.co 3306` |
| `Access denied for user 'incidebd'` | Revisa `DB_USERNAME` y `DB_PASSWORD` en el `.env` |
| `No application encryption key` | `php artisan key:generate --show`, pégalo en `APP_KEY` |
| `403 Forbidden` | `chmod 775 storage bootstrap/cache database/sqlite` |
| `404` en todo | La raíz de nginx debe ser `/var/www/incide/public`, no la carpeta del proyecto |

---

## 9. Entrar como usuario normal

Ya no trabajes como root. Crea tu llave e inhabilita el acceso directo:

```bash
# Saliendo del servidor, desde tu PC:
ssh-keygen -t ed25519 -C "tu@correo"

# scp ~/.ssh/id_ed25519.pub root@TU_IP_VPS:/tmp/millave.pub
```

Y en el servidor:

```bash
mkdir -p /home/incide/.ssh
cat /tmp/mi_llave.pub >> /home/incide/.ssh/authorized_keys
chmod 700 /home/incide/.ssh
chmod 600 /home/incide/.ssh/authorized_keys
rm /tmp/mi_llave.pub

# Desactivar root y contraseñas por SSH
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
```

**Cierra la sesión actual y abre una nueva antes de seguir.** Si algo salió mal,
te quedas fuera y hay que entrar por el panel de recuperación del proveedor.

---

## 10. Actualizar el portal después

Desde tu PC, en la carpeta del proyecto:

```powershell
.\deploy\local\subir.ps1
```

Solo la primera vez indicas el servidor; queda guardado en
`deploy/local/.servidor`. Después basta con el comando.

El script:

1. Detecta qué archivos cambiaron (un ZIP de unos pocos KB)
2. Los sube por `scp`
3. En el servidor limpia caché y recarga PHP

Lo que **nunca** sube: `.env`, la base SQLite y `storage/app` (los PDF
generados y las imágenes en caché).

---

## 11. Mantenimiento

### Ver logs

```bash
tail -f /var/log/nginx/incide-access.log      # quién pidió qué
tail -f /var/log/nginx/incide-error.log       # errores de nginx
tail -f /var/log/php/incide-error.log         # errores de PHP
tail -f /var/www/incide/storage/logs/laravel.log
```

### Reiniciar servicios

```bash
systemctl restart php8.3-fpm
systemctl restart nginx
```

### Liberar espacio

Los PDF generados y las imágenes en caché van creciendo. Con 1.000 reportes
ocupan unos 800 MB.

```bash
du -sh /var/www/incide/storage/app/*
find /var/www/incide/storage/app/Admision -name '*.pdf' -mtime +180 -delete
```

Los PDF de más de 6 meses se pueden borrar: se vuelven a generar si alguien los
pide, y el costo es de nuevo los 70 segundos.

### Actualizaciones de seguridad

```bash
apt update && apt upgrade
reboot    # solo si cambió el kernel
```

### Copia de seguridad

Lo único que no se puede recuperar son los enlaces ya emitidos: están en la base
SQLite. Copia el archivo:

```bash
cp /var/www/incide/database/sqlite/app_data.sqlite /var/backups/$(date +%F).sqlite
```

O una vez al día desde tu PC:

```powershell
scp root@TU_IP_VPS:/var/www/incide/database/sqlite/app_data.sqlite .\respaldo-sqlite.sqlite
```

---

## Antes de publicarlo

Dos cosas que siguen pendientes y que en internet importan:

1. **La contraseña es el número de documento.** Cualquiera que conozca el
   documento de una persona entra a sus reportes clínicos. Hay que cambiarlo:
   un PIN aleatorio enviado por correo o WhatsApp, o verificación por SMS.
2. **`public/test.html` es una página de pruebas sin contraseña.** Mientras esté
   publicada, muestra el logo y el formulario a cualquiera. Bórrala cuando el
   portal real esté listo:

   ```bash
   rm /var/www/incide/public/test.html
   ```

---

## Problema conocido, no del portal

De 973 archivos de imagen revisados, **513 (53%) no existen en el servidor de
Sihos**:

| Año | Disponibles |
|---|---|
| 2026 | **100 %** |
| 2025 | 71,6 % |
| 2024 | 12,4 % |
| 2023 | 13,7 % |
| 2022 | 14,2 % |
| 2021 | 15,1 % |
| 2020 | 20,8 % |
| 2019 | 11,1 % |
| 2018 | 5,5 % |
| 2017 | 2,8 % |
| 2016 | 0 % |

Los pacientes con procedimientos de 2025 en adelante los ven completos. Los de
archivo antiguo verán menos fotos de las que deberían. **Esto se le reporta a
Sihos**, no se arregla desde el portal.