# Cronjobs

> Última actualización: 2026-04-29 (post Sprint 1+2+3)
> Fuente: `crontab -l` real en VPS1 y VPS2 + `app/Console/Kernel.php` del repo + `supervisorctl status`.

Listado completo de tareas programadas en el ecosistema BewPro. Cuatro niveles:

1. **Laravel scheduler local** (corre en cada tenant Laravel donde se invoque `php artisan schedule:run`)
2. **Cron VPS1** (orquestador — `/root/scripts/`)
3. **Cron VPS2** (hosting — backups locales + cron cPanel)
4. **Supervisor (VPS1)** — workers persistentes (queue worker)

---

## 1. Laravel Scheduler — `app/Console/Kernel.php`

Definido en `app/Console/Kernel.php:27-42`. Se dispara cuando algo invoca `php artisan schedule:run` cada minuto (típicamente desde el cron del sistema en cada tenant).

| Comando | Hora | Propósito | Guard |
|---|---|---|---|
| `config:clear` | daily (00:00) | Safety-net multitenant: borra cache stale en CLI/queue contexts | — |
| `bewpro:check-grace` | 09:00 | Verifica grace periods vencidos, suspende si corresponde | — |
| `bewpro:check-renewals` | 10:00 | Trials por vencer (7/3/1 días) + alertas pago manual | — |
| `bewpro:onboarding:cleanup-drafts` | 03:00 | Limpia brand kit drafts > 30 días + assets staging Cloudinary | — |
| `bewpro:healthcheck` | every 6h | HEAD a cada tenant Active, postea fallos a Slack. Usa `--external-dns=8.8.8.8` para bypassear DNS local stale. | `BEWPRO_HEALTHCHECK_ENABLED=true` (solo bewpro22) `withoutOverlapping(15)` |

### Notas sobre el scheduler

- **Sin `withoutOverlapping()`** — si un job se cuelga, el siguiente puede empezar en paralelo.
- **Sin restricción por environment** — corre igual en local/staging/prod.
- **`QUEUE_CONNECTION=sync`** (default en `.env.example`): si un job dispara `dispatch()`, corre síncrono y bloquea el scheduler.
- **Multi-tenant**: cada tenant ejecuta su propio `schedule:run`. Esto significa que estos comandos corren **N veces** (uno por tenant) cada día, no una vez globalmente. Hay que verificar si los comandos `bewpro:check-*` hacen lookup global en Airtable o solo del tenant actual — lo segundo dispararía notificaciones duplicadas.

### Cómo se dispara realmente en producción

> ⚠️ Pendiente confirmar contra los VPS: ¿el cron del sistema en cada cuenta cPanel está configurado para correr `php artisan schedule:run` cada minuto? Esto se hace típicamente con:
> ```
> * * * * * cd /home/<user>/public_html/git-files/<user> && php artisan schedule:run >> /dev/null 2>&1
> ```
> Si **no** está configurado, los 4 jobs anteriores nunca corren. **Verificar en Fase 2** (acceso SSH).

---

## 2. Cron VPS1 — Orquestador

VPS1 corre los scripts maestros que orquestan provisioning, suspensiones y backups de toda la flota.

### 2.1 `process-airtable.sh` — Orquestador de provisioning

| Campo | Valor |
|---|---|
| Path | `/root/scripts/process-airtable.sh` (server: 599 líneas) |
| Repo equivalente | `scripts/bewpro/process-airtable.sh` (~360 líneas) — **DESACTUALIZADO**, le faltan ~240 líneas de lógica dual-VPS |
| Frecuencia real | **`*/5 * * * *`** (validado en `crontab -l` de VPS1) |
| Cron line real | `*/5 * * * * /root/scripts/process-airtable.sh >> /var/log/process-airtable.log 2>&1` |
| Función | Detecta `Pipeline_Status="Required"` en Airtable → balance → provisiona |
| Llama a | (probablemente) `setup_cd_project4.sh` (server tiene v2/v3/v4 + backups; la versión del repo apunta a v2 pero está desactualizada) |

### 2.2 `process_provision_queue.sh` — DEPRECATED 2026-04-29

| Campo | Valor |
|---|---|
| Estado | ✅ **Deshabilitado** — cron comentado en VPS1, script movido a `.archive/2026-04-28/process_provision_queue.sh.deprecated-2026-04-29` |
| Razón | El user cPanel `resellerprueba2` que era productor del queue **no existe** en VPS1. El directorio `/home/resellerprueba2/provision_queue/` nunca fue creado. Cero `queue.log` con history de procesamiento. Ningún tenant Laravel actualmente escribe al directory. |
| Repo (preservado) | `infrastructure/scripts/orchestrator/process_provision_queue.sh` — versión parametrizada (lee `QUEUE_DIR` desde env). Si en el futuro se reactiva el flow form-reseller queue, basta con: <br>1. Setear `QUEUE_DIR=/var/spool/bewpro/queue` en `.airtable.env` <br>2. Crear el dir <br>3. Reactivar cron quitando el comentario en crontab |
| Cron line en VPS (comentada) | `# DEPRECATED 2026-04-29 — * * * * * /root/scripts/process_provision_queue.sh` |
| Pipeline activo de provisioning | El único path activo es `process-airtable.sh` (sección 2.1). Ese consume Stripe webhooks → Airtable → cron. |

### 2.3 `check-subscriptions.sh` — Suspensión por morosidad (REEMPLAZÓ a `process-suspensions.sh`)

| Campo | Valor |
|---|---|
| Path | `/root/scripts/check-subscriptions.sh` (Python script, 16175 bytes post-fix R15) |
| Repo equivalente | `infrastructure/scripts/billing/check-subscriptions.sh` (importado 2026-04-28) |
| Frecuencia real (post-R15 fix) | **`0 8 * * * /root/scripts/check-subscriptions.sh --auto --state=Active --no-restore >> /var/log/check-subscriptions.log 2>&1`** |
| Modos | `--auto` (no pide input), `--state=Active` (filtra Status), `--no-restore` (no reactivar), `--dry-run` (no escribir) |
| Función | (a) Match Airtable Subscriptions contra `/home/*` en **AMBOS VPS** (uno solo orquesta), (b) suspende vía `.htaccess` (no `whmapi1 suspendacct`), (c) reactivación opcional solo en modo interactivo |
| Diferencia con process-suspensions.sh | Suspensión vía `.htaccess` redirect (más rápido y reversible que `whmapi1 suspendacct`). |
| Backup | `/root/scripts/check-subscriptions.sh.bak-pre-cron-fix-20260428` |

### 2.4 Backups — cPanel built-in (NO el `backup-daily-cpanel.sh` del repo)

| Campo | Valor |
|---|---|
| Cron real (VPS1 + VPS2) | `0 2 * * * /usr/local/cpanel/bin/backup` (corre días 0,2,4,6 = Dom/Mar/Jue/Sab según `BACKUPDAYS`) |
| Función | Backup nativo de cPanel/WHM (legacy `cpbackup_legacy`) |
| Destino off-site | **Backblaze B2** bucket `bewpro-backup-server`. <br>VPS1 → `backups/hostinger/`. VPS2 → `backups/donweb/`. <br>Application key en `whmapi1 backup_destination_list`. |
| Retention actual | `BACKUP_DAILY_RETENTION=2` (solo 2 días retenidos en B2). Weekly + Monthly: deshabilitados. `KEEPLOCAL=0` (no copia local post-transport). |
| ⚠️ Issue VPS2 (R8.2) | Log dice `BACKUPACCTS disabled` aunque config global tiene `backupaccts: 1`. Posible config cache stale (mtime config 2026-04-28 16:52). Validar próximo cron 02:00. |
| Estado VPS1 | ✅ 33 backups del 2026-04-28 confirmados en B2 (~33.55 GB). |
| Validar | `whmapi1 backup_destination_list` + `b2 ls bewpro-backup-server` (vía API B2). |

### 2.5 `sync-scripts.sh` — Detector de drift VPS↔repo (R1 final)

| Campo | Valor |
|---|---|
| Path | `/root/scripts/sync-scripts.sh` (canónico en `infrastructure/scripts/utility/sync-scripts.sh`) |
| Frecuencia | `0 7 * * * /root/scripts/sync-scripts.sh --check --slack` (VPS1) |
| Función | Compara archivos canónicos del repo vs `/root/scripts/` y `/home/lacompany/scripts/` del VPS. Si hay drift, postea a Slack. |
| Modos | `--check` (read-only, exit 1 si drift), `--apply` (force repo→VPS), `--slack` (notificación) |
| Log | `/var/log/bewpro/sync.log` |

### 2.4 `email_to_slack.php` — Pipe en tiempo real

| Campo | Valor |
|---|---|
| Path | `/home/lacompany/scripts/email_to_slack.php` |
| Trigger | cPanel Email Forwarders (no es cron — es pipe síncrono cuando llega mail) |
| Función | Forwardea emails de las casillas oficiales a Slack |
| Casillas con pipe | `ventas@`, `proyectos@`, `soporte@`, `contacto@`, `juan@`, `coke@` (todas en lacompaniadigital.com) |

---

## 3. Cron VPS2 — Hosting

**Validado 2026-04-28** contra `crontab -l` real:

- `MAILTO=""`
- Cron de cPanel built-in (idéntico a VPS1): `upcp`, `backup`, `tail-check`, etc.
- **CERO entradas de bewpro** — no corre `process-airtable.sh`, ni `process-suspensions.sh`, ni `check-subscriptions.sh`.
- Pero `/root/scripts/` **sí tiene** copias de `process-airtable.sh`, `process-suspensions.sh`, `process_provision_queue.sh`, `fix-ssl.sh`. Son hot-spares manuales, no se ejecutan.

Implicaciones:
- Si VPS1 cae, **nada** del pipeline funciona en VPS2 hasta que un humano configure el cron ahí.
- Esos scripts pueden estar obsoletos respecto al de VPS1 — riesgo de drift interno.

---

## 4. Cron por cuenta cPanel (cada tenant) — **RESUELTO 2026-04-28**

**Antes**: 0 de 39 tenants tenían `schedule:run` en cron. Los jobs del Laravel scheduler nunca corrían.

**Ahora** (post-R11 fix):
- ✅ 39/39 tenants tienen el cron line:
  ```cron
  * * * * * cd /home/<user>/public_html/git-files/<user> && php artisan schedule:run >> /dev/null 2>&1
  ```
- ✅ Paths no estándar respetados: `bewpro22 → /home/bewpro22/public_html/bewpro`, `peverosa → .../git-files/peverosalud`, `schujman` y `constructoragama → .../git-files`.
- ✅ `setup_cd_project4.sh` y `setup_cd_project2.sh` parcheados con step 11 — futuros tenants se crean con cron desde el inicio.
- Backups del crontab original por tenant en `/tmp/crontab-<user>.bak`.

---

## 5. Queue workers — bewpro22 (R4 cerrado 2026-04-28)

| Campo | Valor |
|---|---|
| Driver activo | `database` (en `.env` de bewpro22) |
| Tabla jobs | Migrada (`bewpro22_bp.jobs` y `bewpro22_bp.failed_jobs`) |
| Workers | **1× supervisor process** `bewpro-queue` |
| Config supervisor | `/etc/supervisord.d/bewpro-queue.ini` |
| Comando worker | `/usr/local/bin/php artisan queue:work --tries=3 --timeout=300 --sleep=3 --backoff=10 --max-jobs=1000 --max-time=3600` |
| User | `bewpro22` |
| Autostart / autorestart | `true / true`. `startretries=999`. |
| Log | `/var/log/bewpro/queue.log` |

**Verificar status**: `ssh vps1-claude "supervisorctl status bewpro-queue"` debería mostrar `RUNNING`.

**Restart después de deploy** (importante — el worker mantiene código en memoria):
```
ssh vps1-claude "su - bewpro22 -c 'cd public_html/bewpro && php artisan queue:restart'"
```

**Otros tenants**: siguen con driver default (`sync`) — ningún otro tenant dispatcha jobs hoy. Si en el futuro algún tenant necesita queue async, replicar este setup en su cPanel.

---

## 6. Comandos artisan disparables manualmente (no son cron)

Documentados aquí como referencia para que no se confundan con cronjobs:

```bash
# Operativos
php artisan bewpro:billing:status [--slack] [--model=Manual]
php artisan bewpro:catalog:seed [--update]
php artisan bewpro:validate [--fix]
php artisan bewpro:cleanup-validation-dbs [--yes]

# Provisioning
php artisan bewpro:new EMAIL "Title" SLUG
php artisan bewpro:provision PATH_AL_JSON
php artisan bewpro:delete CPANEL_USER
```

---

## Resumen unificado — REAL (validado 2026-04-28)

```
VPS1 root crontab:
  */5 * * * *    process-airtable.sh        (orquestador Airtable→provisioning)
  *   * * * *    process_provision_queue.sh  (queue JSON desde form-reseller)
  -2  8 * * *    check-subscriptions.sh      (⚠️ sintaxis inválida — probablemente NO corre)
  0   2 * * *    /usr/local/cpanel/bin/backup (cPanel built-in)
  + ~15 cronjobs nativos cPanel (upcp, exim_tidy, dnsqueue, etc.)

VPS2 root crontab:
  Solo cPanel built-in. NINGÚN bewpro job.

Cuentas cPanel (los 39 tenants):
  Sin crontab. → Laravel scheduler NO ejecuta para ningún tenant.

Email pipes (VPS1 only):
  Email Forwarders cPanel → /home/lacompany/scripts/email_to_slack.php
```

Cosas pendientes a validar más profundo:

1. ¿`check-subscriptions.sh` con sintaxis `-2 8 * * *` realmente no corre? Revisar `/var/log/check-subscriptions.log` para fechas recientes.
2. ¿`backup_destination_list` de cPanel apunta a destino remoto, o solo guarda local? (Riesgo de pérdida si el VPS muere).
3. ¿El driver de queue en bewpro.com es `sync` o `database`? Aún por validar — depende de dónde corre Laravel principal.
