# Koha Humboldt — Migración a Docker

Instancia dockerizada de Koha 25.11 (Ubuntu 20.04) para el catálogo del Instituto Humboldt. Incluye OPAC, intranet, Plack, Zebra, Memcached y RabbitMQ corriendo en un solo contenedor, con perfiles para alternar entre BD local (MariaDB) y BD en la nube (RDS).

## Arquitectura

```
                ┌────────────────────────────────────┐
                │  Contenedor koha                   │
  Browser ──→   │  Apache + Plack + Zebra            │  ──→  MariaDB
  (nginx/ALB)   │  + Memcached + RabbitMQ            │       (local | RDS)
                │  - mod_remoteip lee X-Fwd-For      │
                │  - VirtualHost por dominio (Host:) │
                └────────────────────────────────────┘
```

El contenedor está diseñado para correr detrás de un reverse proxy con terminación TLS. En local se simula con un servicio `nginx`; en producción se reemplaza por un ALB de AWS.

## Requisitos

- Docker Desktop con Docker Compose v2
- OpenSSL (para generar el certificado autofirmado del proxy local)
- Permisos de administrador para editar `C:\Windows\System32\drivers\etc\hosts`
- Permisos de administrador para importar el certificado raíz local

## Setup inicial (una sola vez)

### 1. Copiar el `.env`

```bash
cp .env.example .env
```

Editar `.env` y ajustar las credenciales según el entorno:

| Variable | Valor para BD local | Valor para RDS |
|----------|---------------------|----------------|
| `DB_HOST` | `db` | `<endpoint>.rds.amazonaws.com` |
| `DB_PORT` | `3306` | `3306` |
| `DB_NAME` | `koha_prod_humboldt` | `koha_prod_humboldt` |
| `DB_USER` | `db_koha_prod_humboldt` | el usuario que creaste en RDS |
| `DB_PASSWORD` | tu password local | el password del RDS |
| `DB_ADMIN_PASSWORD` | password root MariaDB local | (no se usa con RDS) |
| `KOHA_OPAC_SERVER_NAME` | `koha.local` | `catalogo.humboldt.org.co` |
| `KOHA_INTRA_SERVER_NAME` | `koha-intra.local` | `intranet.humboldt.org.co` |

### 2. Configurar `/etc/hosts`

Abrir `C:\Windows\System32\drivers\etc\hosts` como administrador y agregar:

```
127.0.0.1  koha.local  koha-intra.local
```

### 3. Generar certificados TLS para el proxy local

```bash
bash docker/gen-local-certs.sh
```

Crea `docker/certs/local.crt` y `docker/certs/local.key`.

### 4. Confiar en el certificado raíz (Windows)

En PowerShell como administrador:

```powershell
Import-Certificate `
  -FilePath "<ruta-al-repo>\docker\certs\local.crt" `
  -CertStoreLocation Cert:\LocalMachine\Root
```

Cerrar y volver a abrir Chrome.

## Ejecución

### Modo local (MariaDB en Docker + proxy nginx con HTTPS)

```bash
docker compose --profile local up --build
```

Activa los servicios `db`, `koha` y `proxy`. El **primer arranque tarda 10-20 minutos**: importa el dump SQL, ejecuta `koha-upgrade-schema`, reindexar Zebra y aplica todos los parches. Los reinicios posteriores son rápidos gracias al `INIT_FLAG` persistido en el volumen.

### Modo RDS (BD en la nube, sin proxy local)

```bash
docker compose up --build
```

Sin `--profile local` solo arranca el contenedor `koha`. Útil para probar contra RDS antes de subir a ECS.

## Acceso a la aplicación

### Con perfil local (recomendado)

| URL | Descripción |
|-----|-------------|
| `https://koha.local` | OPAC (catálogo público) — pasa por nginx, simula el ALB |
| `https://koha-intra.local` | Intranet — pasa por nginx |
| `http://koha.local:8080` | OPAC directo a Apache (sin SSL) — útil para debug |
| `http://koha-intra.local:8080` | Intranet directo a Apache |

### Sin proxy local

| URL | Descripción |
|-----|-------------|
| `http://koha.local:8080` | OPAC directo a Apache |
| `http://koha-intra.local:8080` | Intranet directo a Apache |

> El acceso por `http://localhost:8080` muestra la página default de Apache porque el header `Host` no coincide con ningún VirtualHost. Usar siempre los dominios configurados.

## Comandos útiles

```bash
# Ver logs en vivo
docker compose --profile local logs -f koha

# Ver estado de los servicios
docker compose --profile local ps

# Entrar al contenedor
docker compose --profile local exec koha bash

# Verificar Plack
docker compose --profile local exec koha koha-plack --status prod_humboldt

# Forzar reindexación de Zebra
docker compose --profile local exec koha koha-rebuild-zebra -f -v prod_humboldt

# Parar sin borrar volúmenes (preserva BD local + INIT_FLAG)
docker compose --profile local down

# Borrar todo y forzar reimportación
docker compose --profile local down -v
```

## Health check

El contenedor expone dos endpoints de health check según la fase del ciclo de vida:

| Fase | Puerto | Quién responde | Cuánto dura |
|------|--------|----------------|-------------|
| FIRST_RUN (importación de BD, schema upgrade, reindex) | `8099` | Python `http.server` en background | 10–20 min |
| Operación normal | `80` | Apache → Plack → Koha | indefinido |

El servidor en `:8099` arranca como primera acción del entrypoint y se mata justo antes de que Apache tome el control. Esto evita que ECS marque el task como unhealthy y lo reinicie durante el FIRST_RUN, que es lo más caro de reproducir.

### Verificación local

```bash
# Mientras el contenedor inicializa (FIRST_RUN)
docker compose exec koha wget -qO- http://localhost:8099/
# → starting

# Después de que arrancan los servicios
docker compose exec koha wget -qO- -S http://localhost/ 2>&1 | head -1
# → HTTP/1.1 200 OK
```

### Configuración ECS (task definition)

```json
"healthCheck": {
  "command": [
    "CMD-SHELL",
    "wget -qO /dev/null http://localhost:8099/ 2>/dev/null || wget -qO /dev/null http://localhost/ 2>/dev/null || exit 1"
  ],
  "interval": 30,
  "timeout": 10,
  "retries": 3,
  "startPeriod": 60
}
```

El `||` encadena las dos pruebas: durante el FIRST_RUN responde `:8099`; cuando Apache toma `:80` el primer comando falla y cae al fallback. `startPeriod: 60s` da margen al proceso Python para hacer `bind()`.

### Configuración ALB (target group)

| Parámetro | Valor |
|-----------|-------|
| Protocol | HTTP |
| Port | 80 |
| Path | `/` |
| Healthy threshold | 2 |
| Unhealthy threshold | 5 |
| Interval | 30s |
| Timeout | 10s |
| Success codes | `200-399` |

El ALB chequea `:80`, no `:8099`. Durante el FIRST_RUN el target aparece `unhealthy` en el ALB → simplemente no recibe tráfico (el `startPeriod` del task health check evita que ECS lo reinicie). Cuando Apache arranca, el ALB lo marca `healthy` y empieza a enrutar.

> Importante: el container health check (ECS) y el target group health check (ALB) son independientes. El primero controla el ciclo de vida del task; el segundo, el routing de tráfico.

## Decisiones de diseño relevantes

- **Health check temporal en puerto 8099**: durante el FIRST_RUN (10-20 min) un servidor HTTP en Python responde 200 en `:8099` para que ECS no reinicie el task por timeout. Apache toma `:80` cuando termina.
- **mpm_itk + LD_PRELOAD**: Docker no otorga `CAP_SETGID`, así que `mpm_itk` falla al llamar `initgroups()`. La librería `libfakegroups.so` compilada en el Dockerfile intercepta esa llamada y devuelve 0.
- **mysql-client-8.0**: Ubuntu 20.04 instala el cliente MariaDB por default, que no soporta `caching_sha2_password` de MySQL 8. Se reemplaza con `mysql-client-8.0` para compatibilidad con RDS MySQL 8.
- **Filtros en el dump**: el SQL dump original tiene FKs y GRANTs incompatibles. El entrypoint filtra `CONSTRAINT ... FOREIGN KEY`, `ADD CONSTRAINT`, `GRANT`, `CREATE USER`, etc. antes de pasarlo a `koha-mysql`.
- **Log files pre-creados**: `opac-error.log` e `intranet-error.log` se tocan con ownership `prod_humboldt-koha` antes de iniciar servicios, para que Apache (root) y Plack (instance-user) puedan escribir en los mismos archivos.
- **ALB headers**: `mod_remoteip` confía en `X-Forwarded-For` de RFC 1918; `X-Forwarded-Proto: https` se mapea a la variable `HTTPS=on` para que Koha emita cookies `Secure` y URLs absolutas con esquema correcto.

## Despliegue a producción (ECS)

Resumen — para detalles ver la conversación de migración.

1. **ECR**: build + push de la imagen
2. **RDS MariaDB/MySQL 8**: crear DB y usuario `koha_prod_humboldt` con `ALL PRIVILEGES` (sin `WITH GRANT OPTION`)
3. **EFS**: filesystem con 2 access points (`/koha_data`, `/koha_logs`) con UID/GID 1000
4. **Secrets Manager**: guardar `DB_PASSWORD`
5. **ECS Task Definition**: Fargate, 2 vCPU / 4 GB RAM, montar EFS. Health check según la sección [Health check](#health-check).
6. **ALB**: HTTPS:443 → target group puerto 80 (un solo TG sirve ambos dominios — Apache enruta por `Host` header). HTTP:80 → redirect 301 a HTTPS.
7. **ACM**: certificado con SANs `catalogo.humboldt.org.co` + `intranet.humboldt.org.co`
8. **DNS**: dos CNAME apuntando al DNS name del ALB

## Troubleshooting

### Las URLs se quedan cargando

Revisar los logs de Plack:

```bash
docker compose --profile local exec koha tail -20 /var/log/koha/prod_humboldt/plack-error.log
```

Si dice `Permission denied` en `opac-error.log`, los archivos de log quedaron con ownership root. Fix manual:

```bash
docker compose --profile local exec koha bash -c "
  chown prod_humboldt-koha:prod_humboldt-koha \
    /var/log/koha/prod_humboldt/opac-error.log \
    /var/log/koha/prod_humboldt/intranet-error.log && \
  koha-plack --restart prod_humboldt"
```

### Página default de Apache al entrar a `:8080`

El header `Host` no coincide con ningún VirtualHost. Usar `koha.local:8080` o `koha-intra.local:8080`, no `localhost:8080`.

### Chrome bloquea HTTPS con "Unsafe attempt to load URL"

Limpiar el caché HSTS en `chrome://net-internals/#hsts` para los dominios, o importar el cert raíz local (paso 4 del setup).

### `ERROR 1044: Access denied for user ... to database`

El dump contiene `GRANT` statements que el usuario de BD no puede ejecutar. El entrypoint las filtra, pero requiere rebuild:

```bash
docker compose down -v
docker compose up --build
```

### El container reinicia en loop durante el FIRST_RUN

Si tarda más de los 10-20 min esperados, verificar:
- Conectividad a la BD (`DB_HOST`, `DB_PORT`, security groups en RDS)
- Espacio en disco del volumen de Docker / EFS
- Logs: `docker compose logs koha`
