# Desplegar WEBX Travel en cPanel (sin SSH) — guía paso a paso

Destino: **cPanel compartido, PHP 8.4, MySQL 8.4, sin terminal/SSH**, subida por
**Administrador de archivos** y **phpMyAdmin**. El servidor **ejecuta** la app; no
la compila ni la prueba. Todo se compila en local primero.

> Antes de empezar, en tu máquina deben pasar: `php artisan test`,
> `npm run type-check` y `npm run build`. Ya están en verde (148 pruebas).

El truco clave sin SSH: **los comandos `php artisan` se corren con un Cron Job de
una sola vez** (cPanel → Cron Jobs). Se crea el cron, corre una vez, revisas el log
y **borras el cron**. Lo usaremos para migrar, crear el super-admin, provisionar y
respaldar.

---

## 1. Compilar el paquete en local

```bash
composer install --no-dev --optimize-autoloader
```

```bash
npm run build
```

Arma un ZIP que **incluya** `vendor/` y `public/build/`, y que **excluya**:
`node_modules/`, `.env`, `.git/`, `tests/`, `storage/*.sqlite`,
`storage/tenant-databases/`, `storage/framework/testing/`, `database/central.sqlite`.

> Se sube `vendor/` ya instalado (el server no tiene Composer) y `public/build/`
> ya compilado (el server no tiene Node).

## 2. Subir y ubicar los archivos

En **Administrador de archivos** sube y extrae el ZIP. Deja el código y los secretos
**fuera** de la raíz web:

```
/home/USUARIO/webxtravel/     ← la app Laravel (este repo)
/home/USUARIO/public_html/    ← raíz web del dominio
```

Apunta la raíz web a la carpeta `public/` de la app. Dos opciones:

- **Preferida:** cPanel → **Dominios** → *Document Root* del dominio =
  `/home/USUARIO/webxtravel/public`.
- **Si no puedes cambiar el Document Root:** mueve el **contenido** de
  `webxtravel/public/` dentro de `public_html/` y edita `public_html/index.php`
  para que los dos `require` apunten a `../webxtravel/vendor/autoload.php` y
  `../webxtravel/bootstrap/app.php`.

Asegura que `storage/` y `bootstrap/cache/` tengan permiso de escritura (755/775).

## 3. PHP 8.4 y extensiones

cPanel → **Select PHP Version / MultiPHP Manager** → dominio en **PHP 8.4**.
Habilita: `pdo_mysql, mysqli, openssl, mbstring, curl, fileinfo, gd, intl, bcmath,
sodium, zip`.

## 4. Crear las bases de datos en cPanel

cPanel antepone el prefijo de tu cuenta (p. ej. `USUARIO_webxcentral`).

1. **BD central:** crea `USUARIO_webxcentral` + un usuario con todos los privilegios.
2. **Una BD por agencia (tenant):** crea `USUARIO_webxtenant_<algo>` + usuario con
   privilegios. El hosting compartido **no** deja que la app cree bases sola, por eso
   `TENANCY_MANAGE_DATABASE=false` y las creas tú aquí.

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

Copia `.env.example` a `.env` (Administrador de archivos → Nuevo archivo / renombrar)
y ajusta:

```env
APP_NAME="WEBX Travel"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://tu-dominio.com
APP_KEY=base64:...            # ver paso 6

DB_CONNECTION=central
CENTRAL_DB_DRIVER=mysql
CENTRAL_DB_HOST=127.0.0.1
CENTRAL_DB_DATABASE=USUARIO_webxcentral
CENTRAL_DB_USERNAME=USUARIO_webxadmin
CENTRAL_DB_PASSWORD=********

TENANT_DB_DRIVER=mysql
TENANT_DB_HOST=127.0.0.1
TENANT_DB_USERNAME=USUARIO_webxadmin
TENANT_DB_PASSWORD=********
TENANCY_MANAGE_DATABASE=false

# El dominio principal = Super Admin/plataforma. Cada agencia = un subdominio.
TENANCY_CENTRAL_DOMAINS=tu-dominio.com,www.tu-dominio.com
TENANCY_SUBDOMAIN_BASE=tu-dominio.com

QUEUE_CONNECTION=database
FILESYSTEM_DISK=local
MAIL_MAILER=smtp
MAIL_HOST=...            # tu SMTP (cPanel → Cuentas de correo)
MAIL_PORT=587
MAIL_USERNAME=...
MAIL_PASSWORD=...
```

Nunca subas el `.env`; créalo aquí.

## 6. APP_KEY (crítico y estable)

`APP_KEY` cifra las credenciales de cada tenant y el secreto 2FA, así que se pone
**una vez y no se cambia** (cambiarla invalida esos datos). Genérala en local y pega
el valor en el `.env` del servidor:

```bash
php artisan key:generate --show
```

## 7. Ejecutar comandos artisan sin terminal (patrón Cron de una vez)

cPanel → **Cron Jobs** → crea un cron "cada minuto" con el comando de abajo (ajusta
la ruta de PHP que cPanel te muestre, suele ser `/usr/local/bin/ea-php84`), déjalo
correr **una vez**, revisa `storage/logs/deploy.log` y **borra el cron**:

```
cd /home/USUARIO/webxtravel && /usr/local/bin/ea-php84 artisan core:migrate --force >> storage/logs/deploy.log 2>&1
```

Esto crea el **esquema central** en `USUARIO_webxcentral`. Usa el mismo patrón para
los siguientes comandos (uno a la vez, y borrando el cron después de cada uno).

## 8. Crear el super-admin de plataforma (con 2FA)

Con otro cron de una vez:

```
cd /home/USUARIO/webxtravel && /usr/local/bin/ea-php84 artisan platform:create-admin "tu-correo@dominio.com" --name="Alberto" --role=owner >> storage/logs/deploy.log 2>&1
```

En `storage/logs/deploy.log` verás el **password** y el **secreto 2FA** (más una URL
`otpauth://`). Agrega el secreto a Google Authenticator (o similar), guarda el
password y **borra ese log y el cron**. Ya puedes entrar en
`https://tu-dominio.com/superadmin` con correo + password + código 2FA.

## 9. Provisionar la primera agencia (tenant)

1. En cPanel crea el **subdominio** de la agencia (p. ej. `amaka.tu-dominio.com`) y
   apúntalo al mismo Document Root (`.../webxtravel/public`).
2. En cPanel crea su **BD MySQL** (paso 4).
3. Entra a `/superadmin` → **Provisionar nueva agencia** (nombre, dominio del
   subdominio, correo del admin). El flujo es reanudable e idempotente y **solo deja
   la agencia activa si pasa el smoke test**.
   - Como en compartido la app no crea la BD, registra primero la conexión de esa BD
     (nombre/usuario/clave de cPanel) — se guarda **cifrada con APP_KEY**. Si un paso
     queda pendiente por el hosting, el panel lo marca y puedes reanudar.
4. El admin de la agencia recibe acceso; ellos entran por su subdominio.

## 10. Colas y scheduler (cron permanente)

Dos crons que **sí quedan fijos**:

```
*/5 * * * * cd /home/USUARIO/webxtravel && /usr/local/bin/ea-php84 artisan queue:work --stop-when-empty --max-time=280 >> storage/logs/queue.log 2>&1
```

```
* * * * * cd /home/USUARIO/webxtravel && /usr/local/bin/ea-php84 artisan schedule:run >> /dev/null 2>&1
```

El primero drena la cola (correos, documentos, exportaciones) sin daemon; el segundo
corre el scheduler (con protección anti-solapamiento).

## 11. Actualizaciones futuras

1. En local: `composer install --no-dev -o`, `npm run build`, `php artisan test`.
2. Sube los archivos cambiados (incluye `vendor/` y `public/build/` si cambiaron).
3. Migraciones con cron de una vez:
   - Central: `artisan core:migrate --force`.
   - Tenants: `artisan tenant:migrate-all --dry-run` (inventario) y luego
     `artisan tenant:migrate-all`. Si una agencia falla, **no** se marca actualizada
     y las demás siguen; el reporte lo indica.
4. Borra los `.php` de `bootstrap/cache/` tras cambiar `.env`.

## 12. Respaldos (cron)

```
0 3 * * * cd /home/USUARIO/webxtravel && /usr/local/bin/ea-php84 artisan schedule:run >> /dev/null 2>&1
```

En compartido, los respaldos de MySQL suelen hacerse por **cPanel → Copias de
seguridad** o un cron con `mysqldump` por base (central + cada tenant) + los archivos
privados de `storage/app/tenants`. Cífralos y guarda checksum. **Haz una restauración
de ensayo** periódica (la app trae el servicio de rehearsal). Objetivo: RPO ≤ 24h,
RTO ≤ 4h.

## Checklist de go-live

- [ ] PHP 8.4 + extensiones
- [ ] Document root → `public/`; `storage/` y `bootstrap/cache/` escribibles
- [ ] BD central + BD de la primera agencia creadas en cPanel
- [ ] `.env` de producción; `APP_KEY` presente y **estable**
- [ ] `core:migrate` corrido (cron de una vez, luego borrado)
- [ ] Super-admin creado con 2FA; log de credenciales borrado
- [ ] Primera agencia provisionada por `/superadmin` (smoke test OK)
- [ ] Crons de cola y scheduler activos
- [ ] Respaldo + restauración de ensayo probados
- [ ] Cualquier cron/log temporal de despliegue eliminado
- [ ] `APP_DEBUG=false` y sin rutas de despliegue temporales

Ver también el informe de release: [`docs/RELEASE_CANDIDATE.md`](../RELEASE_CANDIDATE.md).
