# Guía de Despliegue — Hosting Compartido con cPanel

> Objetivo: publicar la plataforma CORDAMI (API Laravel + SPA React) en un hosting
> compartido con cPanel, como entorno de pruebas antes de pasar al VPS.
> Última actualización: 2026-08-21

---

## 0. Pre-requisitos (¡VERIFICAR ANTES DE EMPEZAR!)

| Requisito | Cómo verificarlo | Nota crítica |
|---|---|---|
| **PHP 8.4+** | cPanel → *Select PHP Version* | Laravel 13 exige **PHP ≥ 8.4.1**; si solo hay 8.1/8.2, **no se puede desplegar** (pedir al proveedor o usar VPS). |
| Extensión **pdo_pgsql** | *Select PHP Version → Extensions* | Obligatoria para conectar a PostgreSQL. |
| Extensión **intl, gd, mbstring, opcache** | Extensions | gd la usa el PDF con fotos; intl para fechas. |
| **PostgreSQL disponible** | cPanel → sección *PostgreSQL Databases* (si existe) | ⚠️ **La mayoría de los hostings compartidos solo ofrecen MySQL.** Si no hay PostgreSQL: opciones (a) pedir al proveedor el servicio, (b) una instancia PostgreSQL gestionada externa (la API apuntará a ella; la latencia suele ser aceptable para pruebas), o (c) ir directo al VPS. |
| **PostGIS** (deseable) | Verificar con `SELECT extname FROM pg_extension WHERE extname='postgis';` | SI el hosting PostgreSQL no tiene PostGIS, la app **funciona igual en modo degradado** (solo lat/lng + superficie digitada; se guardan las coordenadas pero no el polígono). Ya está preparado en el código. |
| **SSL instalado** | cPanel → *SSL/TLS Status* | PWA/Service Worker y QR **exigen HTTPS**. Activar Let's Encrypt/AutoSSL. |
| **SMTP para correo** | cPanel → *Email Accounts* | Para verificación de email y recuperación de contraseña. |
| Acceso a **Terminal/SSH** (recomendado) | cPanel → *Terminal* | Facilita artisan/composer/symlink. Sin SSH hay alternativas indicadas. |

---

## 1. Preparar el build (en tu máquina local)

```bash
# 1) Backend: dependencias de producción sin dev (composer puede ejecutarse
#    localmente y se sube el vendor completo, o con composer en cPanel si existe)
cd cordami-platform/backend
composer install --no-dev --optimize-autoloader --prefer-dist

#   Configuración de fechas/locale
cp .env .env.production                    # (plantilla más abajo)

# 2) Frontend: build de producción (los assets quedan en frontend/dist)
cd ../frontend
npm ci
npm run build
```

---

## 2. Base de datos en el hosting

1. **cPanel → PostgreSQL Databases** (si existe) o la consola de tu Postgres gestionado:
   - Crear base: `cordami`
   - Crear usuario: `cordami_app` con contraseña fuerte
   - Asignar TODOS los privilegios de `cordami` a `cordami_app`
   - Anotar: host (p. ej. `localhost` o IP del servicio externo), puerto (5432), base, usuario, clave.
2. ⚠️ Guardar esas credenciales SOLO en el `.env` del servidor (nunca en el repo/chat).

---

## 3. Subir el backend

Con **SSH/rsync** (lo más cómodo):

```bash
# En tu máquina local
rsync -avz --delete --exclude 'storage/framework/*' \
  cordami-platform/backend/ usuario@hosting:~/cordami/backend/
```

Con **File Manager** (sin SSH): comprimir `backend/` en `.zip`, subirlo a `~/cordami/`, y descomprimirlo ahí desde el mismo File Manager.

Estructura final en el home:

```
~/cordami/
└── backend/
    ├── app/
    ├── bootstrap/
    ├── config/
    ├── database/
    ├── public/          ← se convierte en el docroot del dominio
    ├── routes/
    ├── storage/
    ├── vendor/
    └── .env             ← se crea en el paso 4
```

---

## 4. Archivo .env de producción

En `~/cordami/backend/.env` (adaptar valores):

```env
APP_NAME=CORDAMI-Censo
APP_ENV=production
APP_DEBUG=false
APP_KEY=base64:XXXXXXXX   # genera una: php artisan key:generate (local o terminal)
APP_URL=https://tudominio.com
FRONTEND_URL=https://tudominio.com
APP_FORCE_HTTPS=true

LOG_CHANNEL=stack
LOG_LEVEL=warning

DB_CONNECTION=pgsql
DB_HOST=localhost            # o IP del servicio externo
DB_PORT=5432
DB_DATABASE=cordami
DB_USERNAME=cordami_app
DB_PASSWORD=SU_CLAVE_FUERTE

SESSION_DRIVER=database
CACHE_STORE=database
QUEUE_CONNECTION=sync
BROADCAST_CONNECTION=log

FILESYSTEM_DISK=public

MAIL_MAILER=smtp
MAIL_HOST=mail.tudominio.com
MAIL_PORT=587
MAIL_USERNAME=no-reply@tudominio.com
MAIL_PASSWORD=CLAVE_DEL_CORREO
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@tudominio.com
MAIL_FROM_NAME="${APP_NAME}"
```

Sin SSH, puedes crear el `.env` con el **File Manager → Edit** (crear archivo nuevo).

---

## 5. Apuntar el dominio al docroot correcto

Con cPanel NO es necesario tocar `public_html` si cambiamos el **document root**:

1. **cPanel → Domains → Manage**
2. En tu dominio → **Document Root** → cambiarlo a: `cordami/backend/public`
3. Guardar.

> Si el dominio ya tiene otro sitio en su root, alternativas: subdominio nuevo
> (p. ej. `censo.midominio.com`) con docroot `cordami/backend/public`, o subcarpeta
> (requiere ajustar rutas del SPA; no recomendado).

---

## 6. Publicar el frontend (SPA) dentro de `public/`

El SPA y la API comparten el mismo docroot:

```bash
# Local: copiar el build del frontend sobre el public del backend
cp -r cordami-platform/frontend/dist/index.html      cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/assets           cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/*.jpeg           cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/logo.png         cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/manifest.webmanifest cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/sw.js            cordami-platform/backend/public/
cp -r cordami-platform/frontend/dist/workbox-*.js     cordami-platform/backend/public/
# (o simplemente: cp -r frontend/dist/* backend/public/)
```

Luego **subir** esos archivos (rsync o File Manager) a `~/cordami/backend/public/`.

> El SPA ya está preparado: laravel devuelve `public/index.html` para las rutas
> de la app (/censo, /carnet, /login, /admin...) y responde JSON 404 para `/api/*`.

---

## 7. Permisos y enlace de storage

1. Permisos (SSH o File Manager → Change Permissions):
   - `storage/` y `storage/**` → 775
   - `bootstrap/cache/` → 775
   - `vendor/`, `public/` → 755/755 (archivos 644)
2. Crear el enlace del storage (fotos subidas):

   **Con SSH:**
   ```bash
   cd ~/cordami/backend
   ln -s ../storage/app/public public/storage
   ```
   **Sin SSH** — subir este miniscript como `~/cordami/backend/symlink.php`, abrirlo en el navegador una vez y borrarlo:
   ```php
   <?php
   $objetivo = __DIR__ . '/storage/app/public';
   $enlace   = __DIR__ . '/public/storage';
   if (!is_dir($enlace) && is_dir($objetivo)) {
       symlink($objetivo, $enlace);
       echo 'OK';
   } else {
       echo 'Revisar: el enlace ya existe o falta storage/app/public';
   }
   ```

---

## 8. Migraciones + catálogos + usuarios

**Con Terminal/SSH** (recomendado):

```bash
cd ~/cordami/backend
php artisan migrate --force
php artisan db:seed --class=RolesPermisosSeeder --force
php artisan db:seed --class=MirandaCatalogoSeeder --force
php artisan db:seed --class=CatalogoProduccionSeeder --force
php artisan config:clear && php artisan route:clear
```

> Eliminar los seeder de demo (`AdminUserSeeder`) o, mejor, **crear el usuario admin
> de producción** con un password fuerte:
> ```bash
> php artisan tinker --execute="App\Models\User::updateOrCreate(['email'=>'admin@tudominio.com'], ['name'=>'Administración','password'=>'CLAVE_MUY_FUERTE','rol'=>'admin','email_verified_at'=>now()]);"
> ```

**Sin SSH**: si el hosting no da Terminal, subir un `.php` temporal con la conexión Laravel es complejo; lo más práctico es solicitar acceso SSH o pedir a un dev con acceso que ejecute los comandos.

---

## 9. Verificación final

1. Abrir: `https://tudominio.com/api/v1/health` → debe devolver `{"status":"ok",...}`
2. Abrir: `https://tudominio.com/` → debe cargar la landing CORDAMI.
3. Ruta SPA: `https://tudominio.com/censo` → carga el formulario (no 404).
4. `https://tudominio.com/login` → entrar con el admin.
5. `https://tudominio.com/admin` → panel con Dashboard, Censos, Usuarios, Roles.
6. Probar: registra un censo de prueba (con foto), valida el documento como admin, descarga el expediente PDF.
7. Registros/logs: revisar `backend/storage/logs/laravel.log` ante cualquier 500.

---

## 10. Limitaciones del hosting compartido (importantes)

- **PostgreSQL/PostGIS**: si el proveedor no los ofrece → revisar sección 0. La app tiene modo degradado sin PostGIS (coordenadas sí, polígono/superficie por PostGIS no).
- **Workers/cola**: configurado con `QUEUE_CONNECTION=sync` (todo se procesa en la petición). No se necesitan workers.
- **Cron**: no hay tareas programadas en esta versión; en el futuro (exportaciones batch) habría que crear un cron `php artisan schedule:run`.
- **HTTPS**: obligatorio para la PWA. Si el certificado es de un subdominio, la app debe abrirse por HTTPS siempre (el `.htaccess`/`APP_FORCE_HTTPS` ayuda).
- **Subida de archivos**: algunas interfaces limitan el tamaño de subida; si fotos de 10 MB fallan, revisar `upload_max_filesize`/`post_max_size` en *MultiPHP INI Editor* (subirlos a 25M) — o reducir la compresión en el frontend.

---

## 11. Limpieza final

- Borrar `symlink.php` y cualquier archivo temporal subido.
- Quitar accesos de depuración: `APP_DEBUG=false` y `APP_ENV=production` confirmados.
- Rotar credenciales si alguna vez se compartieron fuera del `.env`.

---

## 12. Alternativa recomendada (siguiente paso)

Cuando las pruebas pasen, **desplegar en el VPS con Docker Compose** (PostgreSQL+PostGIS reales,
HTTPS con certificado propio/Let's Encrypt, backups `pg_dump`). El procedimiento de la
Fase de despliegue VPS está documentado en `docs/PLAN_ARQUITECTURA.md` (secciones 8-10).