# Publicar TALENTO UPO en cPanel

El paquete `dist/talento-cpanel.zip` contiene la versión con PHP y MySQL/MariaDB. Las vacantes, perfiles, archivos, postulaciones y permisos se guardan en el servidor. No incluye perfiles demo, archivos de `site/`, pruebas ni credenciales. Se conservó el diseño de vacantes y candidatos.

## 1. Preparar el alojamiento

Usa un dominio o subdominio con certificado HTTPS. Selecciona una versión mantenida de PHP, preferentemente PHP 8.3 o superior, con `pdo_mysql`, `curl`, `openssl`, `mbstring` y `fileinfo`. Base compatible: MySQL 8.0+ o MariaDB 10.4+. La instalación se ha probado localmente con MariaDB 10.4 y PHP 8.0; producción debe usar PHP mantenido.

Descomprime el ZIP en tu equipo. En el administrador de archivos de cPanel, coloca:

```text
/home/USUARIO/
├── talento-private/                 FUERA de public_html
│   ├── app/                         Código PHP
│   ├── config/config.example.php    Plantilla sin credenciales
│   ├── database/talento.sql          Base completa y tres cuentas RH
│   ├── storage/                     Archivos, sesiones y registros privados
│   ├── tools/                       Verificación y tarea automática
│   └── docs/
└── public_html/                     Raíz pública del dominio
    ├── index.html
    ├── paths.php
    ├── api.php, account-api.php, calendar-api.php, document.php, image.php
    ├── assets/, logos/, vendor/
    ├── .htaccess
    └── .user.ini
```

Sube **el contenido** de `public_html` del ZIP a la raíz pública del dominio. No subas el ZIP ni la carpeta privada dentro de esa raíz. Si el subdominio tiene otra ruta, ajusta `$privateRoot` en `paths.php` a `/home/USUARIO/talento-private`. No cambies sitios existentes: utiliza la carpeta del dominio destinado a TALENTO UPO. cPanel sirve los archivos del document root del dominio ([documentación de cPanel](https://docs.cpanel.net/knowledge-base/cpanel-product/how-domains-serve-content/)).

## 2. Crear e importar la base

En **Bases de datos MySQL**, crea una base vacía y un usuario exclusivo para TALENTO UPO; asígnalo a esa base. cPanel suele añadir el prefijo de tu usuario a ambos nombres. Desde phpMyAdmin, selecciona esa base e importa `talento-private/database/talento.sql`.

El SQL crea las tablas y relaciones, categorías iniciales y estas cuentas:

| Nombre | Correo | Rol |
| --- | --- | --- |
| Abigail Flores Quintero | abigail.flores@upotosina.edu.mx | RH |
| SOPORTE TALENTO UPO | soporte.talento@upotosina.edu.mx | RH |
| Georgina Mendez Lopez | georgina.mendez@upotosina.edu.mx | RH |

No hay contraseña compartida ni contraseña provisional en el SQL. Las cuentas se crean con contraseña nula y solo pueden establecerla mediante recuperación por correo. Importar nuevamente las semillas no cambia contraseñas ni roles existentes. No uses este archivo para actualizar una base de otro sistema. No se importa el contenido del antiguo `site/` ni la demo del navegador.

## 3. Configurar el archivo privado

Duplica `config/config.example.php` como `config/config.php`, siempre dentro de `talento-private`:

- `appUrl`: URL final HTTPS del portal, sin `#` ni barra final.
- `database`: host (normalmente `localhost`), puerto, nombre y usuario completos de cPanel y su contraseña.
- `key`: genera una clave ejecutando `php /home/USUARIO/talento-private/tools/generar-clave.php` en Terminal de cPanel y pégala en este campo. Protege y respalda esta clave: cifra la cola de correo y las autorizaciones de Google.
- `storage`: conserva la ruta privada predeterminada.
- `smtp.password`: contraseña de aplicación de **soporte.talento@upotosina.edu.mx**, sin espacios. Conserva Gmail, puerto 587 y TLS de la plantilla. El archivo privado de Windows no se sube automáticamente; configura la credencial en el hosting. No uses la contraseña normal de la cuenta.

Permisos recomendados: configuración `600`, almacenamiento `700`, archivos públicos `644`, directorios públicos `755`, ajustados al usuario bajo el que PHP se ejecuta. Nunca uses `777`. El hosting debe permitir SMTP saliente; la verificación siguiente lo comprueba. Si Workspace bloquea contraseñas de aplicación, será necesaria una autorización de envío compatible antes de habilitar recuperación. No se utiliza Google Calendar para enviar claves.

En **MultiPHP INI Editor**, confirma `upload_max_filesize=50M`, `post_max_size=120M`, `max_file_uploads=12` y `memory_limit=256M`; verifica que `display_errors` esté desactivado. `.user.ini` incluye estos valores, pero algunos proveedores exigen configurarlos en su panel.

## 4. Activar las tareas automáticas

En **Cron Jobs**, agrega una tarea **cada minuto** (`* * * * *`):

```sh
/usr/local/bin/php /home/USUARIO/talento-private/tools/cron.php >> /home/USUARIO/talento-private/storage/cron.log 2>&1
```

Sustituye `USUARIO` y confirma con el hosting la ruta de PHP. Algunas instalaciones usan `/opt/cpanel/ea-php83/root/usr/bin/php`. La tarea envía las claves, reintenta errores transitorios, actualiza entrevistas terminadas y sincroniza Google Calendar. Funciona aunque nadie tenga abierta la web. **Sin cron, los correos permanecerán pendientes.** Consulta [Cron Jobs de cPanel](https://docs.cpanel.net/cpanel/advanced/cron-jobs/).

Comprueba desde Terminal:

```sh
php /home/USUARIO/talento-private/tools/verificar.php --smtp
```

Valida las tablas, las cuentas RH, permisos, ejecución reciente de cron y autenticación SMTP **sin enviar mensajes**. Después comprueba un envío real con una cuenta autorizada. Una autenticación SMTP correcta no acredita entrega en la bandeja de entrada.

## 5. Primer acceso de las tres personas

1. Cada persona abre **Iniciar sesión → ¿Olvidaste tu contraseña?**.
2. Introduce su correo de la tabla anterior. La tarea automática procesa la solicitud normalmente durante el siguiente minuto.
3. Abre el mensaje, inicia sesión con su correo y la clave temporal y elige una contraseña propia.
4. Inicia sesión con la contraseña nueva; verá el panel de RH.

Las claves vencen a los 15 minutos, admiten cinco intentos y solo sirven para definir una contraseña nueva. Se guardan como hash; no se muestran en RH. Hay límites de solicitudes. Comprueba spam si el mensaje no aparece. Los mensajes no se han enviado durante la preparación del paquete.

## 6. Google Calendar

En el archivo privado, completa `google.clientId`, `google.secret` y `google.calendarId` (calendario compartido de RH, no `primary`). En Google Cloud habilita Calendar API y registra exactamente esta URI, sustituyendo el dominio:

```text
https://DOMINIO/calendar-api.php?action=callback
```

Si el portal está en una subcarpeta, incluye esa subcarpeta en `appUrl` y en la URI. Cada persona de RH debe tener permiso para editar ese calendario y conectar su cuenta desde **Entrevistas → icono de Google**. El permiso se conserva cifrado en el servidor entre sesiones hasta desconectarlo manualmente, salvo revocación o vencimiento impuesto por Google. Las autorizaciones de Windows no se copian al hosting.

Una aplicación OAuth externa en modo **Testing** puede recibir refresh tokens que vencen a los siete días; configura correctamente la audiencia y el estado de publicación del proyecto antes de usarlo de forma permanente ([Google OAuth](https://developers.google.com/identity/protocols/oauth2), [acceso sin conexión](https://developers.google.com/identity/protocols/oauth2/web-server)).

Las entrevistas se guardan primero en la base. La web intenta sincronizarlas al guardar si RH está conectado y cron reintenta lo pendiente. La autorización que creó el evento queda asociada a él. Si esa persona desconecta Google, debe reconectarlo para actualizar sus eventos pendientes. Desconectar oculta el calendario de esa persona y conserva el historial. Google envía las invitaciones al sincronizar; puede exigir confirmación del invitado según sus preferencias.

## 7. Comprobación antes de abrir el portal

Verifica en el dominio final: HTTPS, un primer acceso por correo, sesión de RH, publicación de una vacante, registro y postulación de un aspirante de prueba, lectura privada de un PDF y un evento de Calendar autorizado. Consulta `storage/cron.log` y el registro PHP si hay errores; las credenciales y claves no se imprimen allí.

El recorrido es: postulación recibida → en revisión al abrir su perfil o vacante → decisión de continuar o rechazar → entrevista programada → en evaluación al terminar el horario → contratación o rechazo. Azul durante el proceso, rojo al rechazar y verde al contratar. Dos personas que editan simultáneamente reciben una advertencia de conflicto para no sobrescribirse silenciosamente.

La versión contiene los avisos PDF institucionales existentes; confirma con la universidad que corresponden a este portal antes de publicarlo. La infraestructura definitiva, el certificado, las credenciales del hosting y la entrega real de Gmail se validan allí, no desde el paquete local.

## Copias de seguridad y mantenimiento

Respalda juntos la base MySQL, `storage/documents` y `config/config.php`. Las fotos están en `profile_images`; los PDF están en la carpeta privada. Los documentos enviados con una postulación se conservan aunque se sustituya el PDF del perfil. Cron elimina archivos huérfanos de más de 24 horas, claves vencidas antiguas, sesiones antiguas y entradas de cola finalizadas de más de 30 días; no elimina expedientes ni postulaciones.

Rota `storage/cron.log` desde el hosting. Cambiar la clave de cifrado sin migrar datos invalida las conexiones de Google y las solicitudes de correo pendientes. La cola reintenta hasta tres veces; un corte entre envío SMTP y confirmación en la base puede provocar un correo repetido. No hay garantía de entrega instantánea ni de entrega exactamente una vez.
