# Plan de migración a servidor nuevo — xGestión PRD

Borrador, 2026-10-14. Se ejecuta a la vuelta de Claudio, junto con Martín. **Nada de esto está hecho.**

## Por qué

El servidor de producción está comprometido desde, al menos, noviembre de 2025 (detalle en la memoria del incidente, no en el repo). El 14/10/2026 se contuvo lo encontrado, pero un servidor con un año de acceso indebido no es confiable: se arma uno nuevo desde cero y se lleva **solo lo necesario, revisado**, sin copiar nada ejecutable del viejo.

## Qué se migra y cómo

| Origen (servidor actual) | Qué es | Cómo se migra |
|---|---|---|
| Código de la app (`/var/www/html`) | xGestión | **Desde git**, con el deploy de GitHub Actions. Nunca copiado del servidor viejo. |
| Base MariaDB | Todas las bases (`system`, `db_*`, `fly`, `pay`, `backups`…) | Dump revisado. Antes de importar, buscar en los dumps vistas, procedimientos y triggers ajenos (`DEFINER` desconocidos). |
| `/var/www/html/filex/` | Imágenes y archivos públicos | Solo imágenes y documentos, **sin** `.php`, `.phtml`, `.phar`, `.htaccess` ni ejecutables. **Fuera de la carpeta web** (ver abajo). Borrar los `.key`, `.csr` y `.crt` de AFIP que haya (ver hallazgos). |
| `/home/xfiles/<id>/pdfs/` | Comprobantes generados (PDF) | Migrar. Hoy: 139 → 1.165, 1 → 955, 80 → 485, 114 → 146 y algunos más. |
| `/home/xfiles/<id>/fe/` | Archivos de factura electrónica **de 2017** (sistemas 1, 8, 43 y 80) | Migrar como archivo histórico: son comprobantes fiscales. |
| `/home/xfiles/1/temp/` | 6 archivos de 2024 | Revisar y decidir. |
| `/home/xfiles/<id>/tmp/` | Temporales (backups, exportaciones) | **No** migrar. |
| `/home/xfiles/secrets/` | Secreto del push a Motor V3 | Migrar **rotado** (nuevo valor en V3 y en xGestión). |
| `/home/xfiles/backupdbs.txt` | Lista de bases para backup | Migrar. |
| `/home/xfiles/<md5>/` (23 carpetas), `fe/`, `temp/` de la raíz, `<id>/imgs/`, `<id>/temp/` vacías | Restos de versiones anteriores, **vacíos** | **No** migrar. |
| Carpetas vacías de ~40 sistemas | El código las crea al cargar cada sistema | **No** migrar: se recrean solas. |
| `/home/pyafipws/` | Facturación electrónica (Python 2.7) | **Instalar limpio** desde la fuente oficial de pyafipws. Del viejo traer solo los `.ini`, revisados. Comparar `rece1.py` (tenía un ajuste propio) antes de descartarlo. Certificados: ver rotación. |
| crontab de `root` | Tareas del servidor | Revisar línea por línea. En particular el `git pull` cada minuto de `/root/MyScripts` desde GitLab (¿quién controla ese repo?) y `MGS_ProcMonitor.sh` al arrancar. |
| crontab de `www-data` | — | **Ninguno.** El que había era del atacante. |
| Configuración de Apache | vhosts (`XGESTION`, `XGESTION-WEB`, `VERENA`, `VERENAVENDING`, `COPILIBRO`, `DOCS`…) | Rehacer. Separar los sitios en usuarios/carpetas distintos: hoy todos comparten `www-data` y el mismo docroot, y cualquiera es una puerta para los demás. |
| PHP | Versión y extensiones | Revisar `curl.cainfo` (en local apuntaba a un archivo inexistente). `display_errors` apagado, `log_errors` encendido. |

### Imágenes fuera de la carpeta web

Hoy `filex/` está dentro del docroot: un borrado de la web se lleva las imágenes (pasó el 14/10; se recuperaron del snapshot del disco). Propuesta, de menos a más trabajo:

1. **Recomendada:** mover `filex/` a `/home/xfiles/filex/` y publicarlo con un `Alias /filex` de Apache (o un symlink `/var/www/html/filex` → `/home/xfiles/filex`). Las URLs y el código **no cambian**. En esa carpeta, Apache **no ejecuta PHP** (`php_admin_flag engine off` y `Options -ExecCGI` en un `<Directory>`, no en `.htaccess`).
2. Usar el número de sistema en vez de `md5(id)` en las carpetas (`filex/1/i/…`). **No por ahora:** cambian todas las URLs de imágenes y se rompen los links ya publicados (mails, catálogos compartidos, buscadores, MercadoLibre/MercadoPago). Si algún día se hace, con redirecciones `md5 → id`.

### Dónde se usa `filex` en el código (2026-10-14)

Todas arman la ruta `filex/<md5(sys_id)>/<tipo>/` (o `filex/xgestion/icons/` para íconos). Con la opción 1 no hay que tocar ninguna.

| Archivo | Uso |
|---|---|
| `intranet/includes/fns/files.php` | `FILE_GET_FOLDER()` (central), `FILE_IMG_CONVERT_TO_PNG()`, armado de URLs (~165, ~173, ~768) |
| `intranet/includes/fns/sys.php` | `GLOBAL_SYSTEM_PATHS()` (mapa de tipos), `MIGRATE_ALL_HIDDEN_PATHS()` |
| `intranet/includes/fns/imgs.php` | Respaldo de imágenes (`IMG_BACKUP_SYNC_ALL`), restauración, ruta en ~338 |
| `intranet/includes/fns/vmc.php` | Imágenes de productos de Verena, con ruta y dominio fijos (~2547, ~3923: `https://www.verena.com.ar/filex/...`) |
| `intranet/includes/fns/productos.php` | `PG_MAIN_IMG()`: arma la URL de la imagen dentro del SQL |
| `intranet/includes/cfg/icons.php` | `ICO_UALA` → `/filex/xgestion/icons/500001.png` |
| `intranet/sys/mods/files/process.php` | `IMG_COMPRESS()` (zip de imágenes) |
| `intranet/sys/admin/filex_overview.php` | Pantalla de administración "Archivos en /filex/" |
| `intranet/sys/admin/testings.php` | Pruebas |
| `site/payments/collect.php`, `intranet/checkout/test.php` | `picture_url` fija a `https://www.xgestion.com.ar/filex/c4ca…/1/800` |

En el repo de Humming (front y Motor V3) no hay referencias a `filex`. **Falta revisar la base:** URLs a `/filex/` guardadas en datos (plantillas de mail, contenido del sitio, publicaciones).

## Hallazgos de código a corregir (antes o durante la migración)

1. **Certificados de AFIP en la carpeta pública — prioridad alta.** `intranet/sys/mods/config/fe_download.php` escribe la clave privada (`.key`), el CSR y el certificado de la empresa en `filex/<md5>/2/<CUIT> <alias>.key` para descargarlos, y **nunca los borra**: quedan accesibles por URL sin sesión, con un nombre previsible. Además genera el archivo **antes** del `CHK_ACCESS` (sin sesión no llega porque `control.php` redirige antes, pero cualquier usuario de la intranet puede bajar la clave). Corregir: generar el contenido en memoria y mandarlo como descarga, sin archivo, y solo con rol de administración. Verificado el 14/10/2026: en el `filex/` actual del servidor no hay `.key`, `.csr` ni `.crt` (después del borrado y la restauración). El problema sigue en el código: cada descarga de certificados vuelve a dejar el archivo.
2. **Documentos (tipo 2) a medio migrar.** `GLOBAL_SYSTEM_PATHS()` dice que van ocultos en `xfiles/<id>/pdfs/`, pero `files.php` (~394, ~433, y los links ~552–820), `mail.php` (~1062), `send_types.php` y `fe_download.php` siguen usando `FILE_GET_FOLDER(FILE_TYPE_DOCUMENT)`, es decir la carpeta **pública**. Unificar en la ruta oculta.
3. **`exit()` al no poder crear una carpeta** en `SYS_PATH_TEMP()`, `SYS_PATH_PDFS()` y `LOAD_SYS_PATHS()` (`sys.php`): mismo problema que trabó el cron el 14/10 con `FILE_GET_FOLDER()` (ya corregido en `ebb291aa`). Registrar en `pay.log` y seguir.
4. **Ruta fija en `system/tools/cron.php`:** incluye `/home/claudiom/html/intranet/includes/config.php`, pero el sitio vive en `/var/www/html`. Usar `__DIR__`.
5. **Dominios y rutas fijas** en `vmc.php`, `collect.php` y `checkout/test.php` (ver tabla de `filex`).

## Rotación de credenciales (todas, desde el servidor nuevo)

- Usuario de MariaDB de la aplicación (además estuvo expuesto por un endpoint de backup sin autenticación, corregido en `2b0752cc` y `dec4afdc`).
- Claves SSH de los usuarios del servidor y la del deploy (GitHub Secrets).
- Tokens de `system.apis`.
- Secreto del push de operadores a Motor V3.
- MercadoPago, UALA, MercadoLibre y cualquier otra API configurada.
- **Certificados de AFIP de cada CUIT:** generar clave y CSR nuevos, subir el certificado en AFIP y dar de baja los viejos. Requiere la clave fiscal de cada empresa: se coordina con ellas. Hasta entonces el servidor nuevo puede usar los actuales.

## Backups

- La política de snapshots diarios del Lifecycle Manager funcionó (de ahí se recuperó `filex/`). Conservar `snap-02be52ef716913122` (13/10) como evidencia.
- Sumar copias fuera del alcance del servidor: otra cuenta de AWS o S3 con Object Lock.
- Imágenes: `backups.images` (`IMG_BACKUP_*`) + tarea diaria `IMG_BACKUP_SYNC_ALL` en `system.cron`.

## Verificación después de migrar

- [ ] El sitio y la intranet responden en todos los dominios.
- [ ] El cron corre (`system.config.cron_running` vuelve a 0 después de cada tarea).
- [ ] Facturación electrónica: autorizar un comprobante de prueba.
- [ ] Imágenes: se ven, y `IMG_BACKUP_SYNC_ALL` las respalda.
- [ ] No hay procesos de `www-data` fuera de Apache ni crontab de `www-data`.
- [ ] En `filex/` no se ejecuta PHP (probar con un archivo de prueba y borrarlo).
- [ ] El servidor viejo queda apagado (no borrado) hasta cerrar la investigación.
