# Despliegue en producción

El portal va en el mismo servidor de SGO, como un sitio aparte: `clientes.hormimax.cl`.

## 1. Código

```bash
cd /var/www/html
git clone <repositorio> portal_clientes
```

## 2. Base de datos (en este orden, con un usuario administrador de MySQL)

1. `sql/01_tablas.sql`: tablas `portal_*`.
2. `sql/02_vistas.sql`: vistas `portal_v_*`. Son `SQL SECURITY DEFINER`: quedan a nombre del usuario
   que las crea, y ese usuario debe seguir existiendo y tener lectura sobre las tablas de SGO.
3. `sql/03_usuario_bd.sql`: usuario `portal_api@localhost` y sus permisos. **Antes de correrlo**,
   cambiar `CLAVE_LARGA_AL_AZAR` por una clave propia. MySQL exige mayúsculas, minúsculas, números
   y un símbolo.

`sql/instalar_dev.php` es solo para desarrollo: no correrlo en producción.

## 3. Configuración

```bash
sudo mkdir -p /etc/portal_clientes
sudo cp config/config.ejemplo.php /etc/portal_clientes/config.php
sudo chown root:www-data /etc/portal_clientes/config.php
sudo chmod 640 /etc/portal_clientes/config.php
sudo nano /etc/portal_clientes/config.php
```

Valores a completar:

| Valor | Qué poner |
|---|---|
| `bd.clave` | La clave elegida en el paso 2.3. `bd.host` debe quedar en `localhost`. |
| `clave_admin` | `php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"` |
| `clave_codigos` | Otra llave al azar, generada igual. Con ella se cifran los códigos públicos de documentos, obras y clientes. **No se cambia nunca después de salir a producción:** cambiarían todos los códigos y los enlaces que los clientes tengan guardados dejarían de servir. Se respalda junto con este archivo. Sin ella la API responde error 500. |
| `url_api_interna` | `https://clientes.hormimax.cl/api/v1` (ver paso 5) |
| `correo.phpmailer` | `/var/www/html/sgo/mailer/PHPMailerAutoload.php` |
| `correo.smtp_conf` | El archivo de la cuenta de correo que ya usa SGO |
| `url_sgo_interna` | `https://sgo.hormimax.cl/modules/portal_clientes/api/dte_pdf.php`. Por ahí el portal le pide a SGO la guía o factura original cuando no hay copia guardada. SGO solo acepta esa llamada desde `127.0.0.1`, por eso el portal resuelve ese nombre al propio servidor (`url_sgo_interna_ip`, que se deja en `127.0.0.1`). |
| `alertas_correo` | Casilla de Hormimax que recibe los avisos de seguridad (alguien probando códigos de documentos). Sin ella el aviso queda solo en el registro de accesos y en el log del servidor. |
| `geocercas_correo` | Casilla de comercial que recibe cada lunes la lista de obras sin geocerca (ver 6.3). |

## 4. Sitio en Apache

El sitio publica **solo** la carpeta `web/`. La API entra por una regla, y el resto del proyecto
(`lib/`, `sql/`, `config/`, `tests/`) queda fuera del alcance del navegador.

```apache
<VirtualHost *:80>
    ServerName clientes.hormimax.cl
    DocumentRoot /var/www/html/portal_clientes/web

    <Directory /var/www/html/portal_clientes/web>
        Options -Indexes
        AllowOverride None
        Require all granted
    </Directory>
    <Directory /var/www/html/portal_clientes/api>
        Options -Indexes
        AllowOverride None
        Require all granted
    </Directory>

    RewriteEngine On
    RewriteRule ^/api/v1(/.*)?$ /var/www/html/portal_clientes/api/index.php [L]

    # Para que los sistemas de los clientes puedan usar Authorization: Bearer
    SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

    # Una página que no existe muestra la página de error del portal, no la de Apache.
    # (Las direcciones de la API que fallan las resuelve la propia API con la misma página: lib/pagina.php)
    ErrorDocument 404 /404.html
    ErrorDocument 403 /404.html

    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "DENY"
    Header always set Referrer-Policy "no-referrer"

    # Las páginas se revisan siempre contra el servidor: así un cambio se ve al tiro.
    # portal.js y portal.css van con sello de versión (?v=...), por eso pueden guardarse un año.
    <FilesMatch "(\.html|version\.json)$">
        Header set Cache-Control "no-cache"
    </FilesMatch>
    <FilesMatch "\.(js|css)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>

    ErrorLog ${APACHE_LOG_DIR}/portal_clientes_error.log
    CustomLog ${APACHE_LOG_DIR}/portal_clientes_access.log combined
</VirtualHost>
```

```bash
sudo a2enmod rewrite headers
sudo a2ensite clientes.hormimax.cl
sudo apache2ctl configtest && sudo systemctl reload apache2
sudo certbot --apache -d clientes.hormimax.cl
```

Antes de esto, el dominio `clientes.hormimax.cl` debe apuntar a la IP del servidor.

Después de Certbot, agregar en el sitio `:443`:

```apache
Header always set Strict-Transport-Security "max-age=31536000"
```

## 5. Que SGO llegue a la API sin salir a internet

La administración solo se acepta desde el mismo servidor. Para que SGO llame a la API por dentro:

```bash
echo "127.0.0.1 clientes.hormimax.cl" | sudo tee -a /etc/hosts
```

Con eso `url_api_interna = https://clientes.hormimax.cl/api/v1` resuelve al propio servidor, con el
certificado válido, y la API ve la llamada venir de `127.0.0.1`.

## 6. Pantalla de administración en SGO

Archivos en SGO:

```
modules/portal_clientes/portal_clientes.php
modules/portal_clientes/core/gate_portal.php
modules/portal_clientes/ajax/ajax_portal.php
```

En `core/gate_portal.php` el proceso está en `0`: **en producción no entra nadie hasta asignar el
número de proceso**. Una vez creado y asignado el proceso en SGO, cambiar ahí el número.

## 6.1 En cada actualización

Las páginas ya vienen selladas desde desarrollo, junto con `web/version.json`. Para comprobarlo en el servidor:

```bash
php herramientas/sellar.php --revisar
```

## 6.2 Copia del estado de cuenta

El archivo `cron/cron_portal_cxc.php` va en SGO. Se agrega al cron de `www-data`, 3 veces al día:

```
10 7,13,19 * * * php /var/www/html/sgo/cron/cron_portal_cxc.php >> /var/log/portal_cxc.log 2>&1
```

El archivo de registro debe existir y ser de `www-data`, o el cron falla sin avisar:

```bash
sudo touch /var/log/portal_cxc.log && sudo chown www-data /var/log/portal_cxc.log
```

La primera vez conviene correrlo a mano para no esperar al horario:

```bash
sudo -u www-data php /var/www/html/sgo/cron/cron_portal_cxc.php
```

## 6.3 Camión en ruta

No necesita nada propio: lee la posición que ya guarda el cron de geocercas de SGO
(`customer/indemax/cron/cron_chequeo_geocercas.php`, cada minuto, en la crontab de root). Si ese cron se
detiene, el portal deja de mostrar posiciones a los 30 minutos y muestra los camiones "sin señal".

Aviso semanal de obras sin geocerca, en la crontab de `www-data`:

```
0 8 * * 1 php /var/www/html/portal_clientes/cron/aviso_geocercas.php >> /var/log/portal_geocercas.log 2>&1
```

El archivo de log debe existir y ser de `www-data` antes de la primera corrida. Para ver la lista sin
enviar nada: `php cron/aviso_geocercas.php --ver`.

El mapa usa Leaflet (cdnjs) y las imágenes de OpenStreetMap. Si el tráfico crece conviene contratar un
proveedor de mapas: se cambia en una línea de `web/ruta.html` (`TESELAS`).

## 7. Comprobación

Primero, la revisión automática. No cambia nada ni muestra claves; dice qué falta y cómo arreglarlo:

```bash
cd /var/www/html/portal_clientes
sudo -u www-data php herramientas/revisar_servidor.php
```

No abrir el portal a los clientes mientras muestre alguna ✗. Después, a mano:

1. `https://clientes.hormimax.cl` muestra el ingreso.
2. `https://clientes.hormimax.cl/api/v1/condiciones` responde JSON.
3. `https://clientes.hormimax.cl/lib/nucleo.php` y `/sql/01_tablas.sql` responden 404.
4. En SGO (Hormimax), la pantalla Portal de clientes lista los clientes sin error.
5. Habilitar un cliente, crear un usuario con un correo propio, y completar el alta con el enlace.

## Qué revisar si algo falla

| Síntoma | Causa habitual |
|---|---|
| La pantalla de SGO dice que la API no respondió | Falta la línea de `/etc/hosts`, o `url_api_interna` apunta mal |
| "Acceso no autorizado" desde SGO | `clave_admin` distinta, o la llamada no llega desde `127.0.0.1` |
| La API responde error 500 al partir | `www-data` no puede leer `/etc/portal_clientes/config.php` |
| "Access denied" en el log de PHP | `bd.host` en `127.0.0.1` en vez de `localhost`, o clave distinta a la del paso 2.3 |
| Los correos no salen | Ruta de `correo.smtp_conf` o de `correo.phpmailer` equivocada; la pantalla entrega el enlace para enviarlo a mano |
