# KIA: instalación nueva en MariaDB y cPanel

El proyecto utiliza SQLite en local y `mysql+pymysql` con MariaDB en producción. No se requiere importar la base de datos local. No copies kia.db, baseoriginal.db, .kia-secret, respaldos, logs ni credenciales al directorio público.

## 1. Requisitos del hosting

- Aplicaciones Python WSGI con Passenger habilitadas; Python 3.11 o superior.
- MariaDB con tablas InnoDB y codificación utf8mb4.
- HTTPS activo para el dominio antes de usar cuentas reales.
- Acceso a terminal y cron, o asistencia del proveedor para ejecutar los comandos.
- Confirma `max_user_connections` y cuántos procesos Passenger permite tu cuenta.

No todos los planes de cPanel habilitan Python. La configuración exacta depende del proveedor: [Application Manager](https://docs.cpanel.net/cpanel/software/application-manager/) y [Python WSGI en cPanel](https://docs.cpanel.net/knowledge-base/web-services/how-to-install-a-python-wsgi-application/).

## 2. Base de datos desde cero

En cPanel crea una base nueva y un usuario dedicado. Asigna permisos sobre esa base, nunca permisos globales. Conserva el prefijo de usuario que cPanel agrega a los nombres. Para la inicialización necesita permisos de creación y alteración; después el usuario de ejecución puede limitarse a SELECT, INSERT, UPDATE y DELETE.

Sube el código a una carpeta privada de la cuenta, fuera de public_html. Registra la aplicación y su URL desde el panel. Selecciona:

- Archivo de inicio: `passenger_wsgi.py`.
- Objeto WSGI: `application`.
- Entorno: producción.

Activa el entorno virtual que muestre tu proveedor y ejecuta dentro de la carpeta del proyecto:

```bash
python -m pip install -r requirements.lock
python -m flask --app app init-db
```

Antes del segundo comando deben estar configuradas las variables del siguiente apartado. `init-db` crea tablas, restricciones, configuración base y el premio interno de bienvenida. No crea clientes, productos, recargas ni premios comerciales de ejemplo. Una instalación nueva inicia vacía.

Como alternativa, `deploy/mariadb-schema.sql` contiene únicamente el DDL para una base vacía. Después de importarlo, ejecuta también `init-db` para crear la configuración y fila de bloqueo. No ejecutes el SQL de instalación sobre una base existente.

La app ya no ejecuta `create_all`, migraciones ni reparaciones de usuarios al arrancar o al visitar páginas.

## 3. Variables y credenciales

Usa `deploy/cpanel.env.example` como lista de variables para Application Manager. El archivo es una plantilla, no se carga automáticamente. Configura también las variables en el entorno de los comandos y cron mediante un archivo privado fuera del directorio público.

`DATABASE_URL` tiene el formato:

```text
mysql+pymysql://USUARIO:CONTRASEÑA_CODIFICADA@localhost/BASE?charset=utf8mb4
```

Codifica caracteres especiales de usuario y contraseña como componentes URL. No pegues valores reales en tickets, capturas o reportes.

Genera localmente la clave de sesión:

```bash
python -c "import secrets; print(secrets.token_hex(32))"
```

Genera el hash de contraseña administrativa sin escribir la contraseña en el historial:

```bash
python -c "from getpass import getpass; from werkzeug.security import generate_password_hash; print(generate_password_hash(getpass('Nueva contraseña administrativa: ')))"
```

Para el segundo factor, genera un secreto y regístralo en tu aplicación autenticadora (TOTP, SHA1, 6 dígitos, 30 segundos):

```bash
python -c "import pyotp; print(pyotp.random_base32())"
```

Guarda ese secreto en `KIA_ADMIN_TOTP_SECRET`. El acceso administrativo en producción exige contraseña y código TOTP; un mismo código no se puede reutilizar. Cambiar el hash o secreto TOTP invalida las sesiones administrativas. Guarda una copia de recuperación en un gestor de contraseñas.

### Crear el primer administrador en cPanel

El administrador de producción no es una fila de la base de datos. Se define antes de iniciar la aplicación mediante tres variables privadas: `KIA_ADMIN_USERNAME`, `KIA_ADMIN_PASSWORD_HASH` y `KIA_ADMIN_TOTP_SECRET`. Genera el hash y el secreto con los comandos anteriores, agrega las tres variables en **Setup Python App / Application Manager**, guarda los cambios y reinicia la aplicación. Después entra en:

```text
https://TU_DOMINIO/#/pages/admin/index
```

Usa el nombre configurado, la contraseña original con la que generaste el hash y el código de seis dígitos de tu autenticador. No ejecutes `create-local-admin` en el hosting: ese comando rechaza el modo producción y está destinado únicamente a desarrollo.

`KIA_ALLOWED_HOSTS` debe contener únicamente los dominios reales separados por comas. `KIA_PROXY_HOPS=0` es la opción conservadora. Si el hosting termina TLS en un proxy, confirma con el proveedor qué encabezados sanea y cuántos proxies confiables hay antes de configurarlo. Nunca confíes ciegamente en X-Forwarded-For enviado por clientes.

## 4. Conexiones y transacciones

MariaDB usa un pool de **2 conexiones por proceso, sin overflow**, tiempo de espera de 10 segundos y comprobación de conexiones. No lo aumentes sin conocer `max_user_connections`. Presupuesta procesos web × 2, además de cron y administración. Si el proveedor permite menos conexiones, reduce procesos y revisa el presupuesto antes de activar la aplicación.

En MariaDB, cada POST adquiere primero una fila de bloqueo estable para su cuenta; usuarios distintos pueden escribir en paralelo. Las compras bloquean comprador y beneficiarios en orden ascendente de ID antes de tocar saldos. Administración y mantenimiento usan un bloqueo conservador cuando no hay una cuenta concreta. SQLite conserva `BEGIN IMMEDIATE` porque sólo admite un escritor. Las restricciones únicas y el control optimista de versión siguen rechazando pagos repetidos o escrituras obsoletas.

Los cambios de saldo, resultado, auditoría y comprobante idempotente se confirman juntos. Los `commit` de las rutas se convierten en `flush` hasta ese límite. No añadas llamadas HTTP, SMS, correo ni pasarelas dentro de la transacción; usa una tarea posterior u outbox al integrar servicios externos.

Los comprobantes de idempotencia no se borran automáticamente. La misma clave y el mismo contenido devuelven el mismo resultado; una clave reutilizada con datos distintos se rechaza. Los clientes externos deben enviar `Idempotency-Key`. Los formularios y fetch internos ya lo hacen.

Los índices compuestos cubren los listados y totales habituales por usuario, estado y fecha. Los historiales de usuario muestran como máximo los 100 movimientos más recientes por consulta. Los archivos estáticos se cachean durante una hora en producción y la recarga automática de plantillas queda desactivada.

`KIA_DB_POOL_SIZE` queda en 2 y `KIA_DB_MAX_OVERFLOW` en 0. Sólo cámbialos después de confirmar `max_user_connections`. El máximo teórico de conexiones es procesos Passenger × (`pool_size + max_overflow`), más cron y herramientas administrativas.

## 5. Cron

Configura, por ejemplo, cada cinco minutos, usando el Python del entorno virtual y las mismas variables privadas:

```bash
cd /home/TU_USUARIO/kia && /RUTA/DEL/VIRTUALENV/bin/python -m flask --app app maintenance --limit 100
```

Esta tarea limpia por lotes límites de intentos vencidos. No acredita dinero, ni borra auditoría o comprobantes. `reconcile-fortune --after-id 0 --limit 100` emite un reporte de discrepancias sin modificar saldos; para bases grandes avanza el cursor `last_id` entre ejecuciones. No lances múltiples conciliaciones completas concurrentes.

Las ganancias siguen siendo reclamadas por POST por el usuario. Los GET no crean premios ni recalculan y persisten billeteras. La conciliación es una tarea administrativa, no parte de abrir un sobre.

## 6. Respaldo y restauración

Configura copias automáticas de MariaDB con las herramientas del hosting, fuera del directorio público. Incluye también imágenes, variables/credenciales en un almacén seguro y versión desplegada. Mantén copias fuera del servidor y cifra el destino. Una copia no está validada hasta restaurarla en otra base y comprobar saldos, historial y acceso.

Antes de actualizar una base existente: respaldo consistente, detén escrituras, ejecuta `init-db`, revisa restricciones y reinicia. Si hay pagos o compras duplicadas históricas, el índice único rechazará la actualización: no se borran movimientos automáticamente; concílialos antes de continuar. La nueva auditoría comienza desde esta versión, no reconstruye autoría histórica.

## 7. Validación antes de publicar

- Prueba alta, verificación del titular, login/MFA, cambio de contraseña y cierre de sesiones antiguas.
- Prueba compra, retiro, canje y apertura con la misma clave enviada dos veces.
- Comprueba HTTPS, cookies Secure, CSP, dominio permitido y protección de archivos privados.
- Ejecuta las pruebas de concurrencia en una base de staging MariaDB separada. La suite entregada usa SQLite aislado; la compatibilidad DDL no sustituye probar contra tu servidor real.
- Verifica una restauración de respaldo y los límites reales del hosting.

Prueba primero una URL de lectura en staging y aumenta la concurrencia poco a poco:

```bash
python tools/load_smoke.py --url https://staging.ejemplo.com/register --requests 500 --concurrency 25
```

No ejecutes una prueba de carga contra producción sin confirmar los límites y reglas del proveedor. Mide CPU, memoria, conexiones MariaDB, respuestas por segundo, p95 y errores. Después prueba POST con cuentas de staging y claves de idempotencia distintas; no uses dinero o usuarios reales.

Las cuentas nuevas no reciben beneficios hasta que el administrador confirma la verificación del titular. No se ha integrado SMS. VIP exige invitación directa verificada, cuenta activa, recarga aprobada mínima y compra. Los retiros requieren cuenta verificada. Esta decisión evita otorgar saldo retirable solo por registros.

## Referencias

[SQLAlchemy: MariaDB/MySQL](https://docs.sqlalchemy.org/en/21/dialects/mysql.html), [cPanel Application Manager](https://docs.cpanel.net/cpanel/software/application-manager/), [cPanel Python WSGI](https://docs.cpanel.net/knowledge-base/web-services/how-to-install-a-python-wsgi-application/).
