Gestión de inquilinos¶
Disponible solo para: Platform Admin
Un tenant es una sola empresa que usa Hardhat Flow. Cada inquilino opera en aislamiento total — tiene su propia base de datos MySQL dedicada, sus propios usuarios y sus propios datos. Ningún inquilino puede ver ni acceder a registros de otro.
Qué es un inquilino¶
En la arquitectura multi-inquilino de Hardhat Flow:
- Cada inquilino corresponde a una empresa (ej. "Acme Construction").
- Cada inquilino tiene una base de datos MySQL dedicada alojada en el clúster MySQL administrado de DigitalOcean.
- Las credenciales del inquilino (host, nombre, usuario, contraseña de BD) se guardan cifradas en la base de datos de plataforma — una BD separada a la que solo la aplicación y los Platform Admins acceden.
- El enrutamiento por URL usa el slug del inquilino para saber a qué base de datos conectar en cada solicitud.
El aislamiento por inquilino no es negociable. Nunca se permiten consultas entre inquilinos. El enrutador de BD lo aplica en la capa de aplicación.
Campos del inquilino¶
| Field | Description |
|---|---|
name |
Nombre para mostrar completo de la empresa (ej. "Acme Construction LLC"). |
slug |
Identificador seguro para URL usado en el enrutamiento (ej. acme-construction). Debe ser único entre todos los inquilinos. Inmutable tras la creación. |
created_at |
Marca de tiempo cuando se provisionó el inquilino. |
is_deleted |
true si el inquilino fue eliminado de forma lógica. Los inquilinos eliminados devuelven 404 en todas las solicitudes. |
deleted_at |
Marca de tiempo de la eliminación lógica (nulo para inquilinos activos). |
user_count |
Número de usuarios no eliminados en la base del inquilino (solo en el endpoint de detalle). |
Cómo funciona el enrutamiento por inquilino¶
Todas las rutas de API acotadas al inquilino usan rutas planas (sin slug en la URL):
https://hardhatflow.com/api/v1/...
En cada solicitud:
- Se verifica el JWT de Auth0 y se carga o crea el registro local
User. - El campo
companydel usuario (definido al aprovisionar la cuenta) identifica el inquilino. - Se recuperan y descifran las credenciales de la base del inquilino.
- El enrutador de Django dirige todas las consultas de esa solicitud a la base del inquilino.
Si el usuario autenticado no tiene empresa (y no es Platform Admin), la solicitud se rechaza con 401 {"code": "no_tenant"}.
Invariante usuario–inquilino¶
Todo usuario que no sea administrador debe estar asociado a exactamente un inquilino. Esto se aplica en el momento de la autenticación:
- Si el JWT no trae reclamo de rol, la autenticación falla de inmediato con
code="no_tenant". - Tras cargar el usuario, si
user.company_idesNoney el usuario no es Platform Admin, la autenticación falla con la misma respuestacode="no_tenant". - Los Platform Admins (
role=platform_admin) pueden tenercompany=None; usan endpoints de administración de la plataforma y no rutas acotadas al inquilino.
Los usuarios que ven este error son redirigidos a /account-error/ en la interfaz, donde se les indica que contacten a su administrador.
Sin registro automático¶
No existe flujo de alta automática de inquilinos. Cada inquilino debe ser provisionado por un Platform Admin.
Los clientes potenciales se incorporan manualmente:
- Ventas o soporte confirma al nuevo cliente.
- Un Platform Admin crea el registro del inquilino en el panel de administración.
- La base de datos se provisiona automáticamente.
- El Platform Admin configura CompanySettings (tasa de impuesto, número Twilio, etc.) y crea la primera cuenta Owner.
- A partir de ahí el Owner administra sus propios usuarios.
Aprovisionar un nuevo inquilino¶
Cuando un Platform Admin crea un nuevo inquilino:
- Se crea el registro del inquilino en la base de plataforma con
name,slugy credenciales de BD generadas. - Se crea una nueva base MySQL en el clúster MySQL administrado de DigitalOcean.
- Las migraciones de Django se ejecutan de inmediato contra la nueva base para inicializar las tablas base.
Todo el proceso de aprovisionamiento está automatizado. No se requiere configuración manual de base de datos.
Panel del inquilino (UI Platform Admin)¶
Los Platform Admins tienen un panel en la aplicación para ver todos los inquilinos de un vistazo y profundizar en cualquier inquilino para soporte.
Lista de inquilinos (/platform-admin/tenants/)¶
Muestra una cuadrícula de tarjetas de cada inquilino no eliminado. Cada tarjeta incluye:
- Nombre de empresa y slug
- Indicador de salud de BD — verde (alcanzable) o rojo (no alcanzable) con mensaje de error al pasar el cursor
- Recuento de usuarios (activos, no eliminados)
- Total de Bids, total de Jobs e Invoices abiertos
Las tarjetas enlazan a la página de detalle del inquilino.
Detalle del inquilino (/platform-admin/tenants/{id}/)¶
Vista completa de observabilidad para un inquilino, organizada en cuatro secciones:
1. Fila KPI — Usuarios activos, total de Bids, total de Jobs, Invoices abiertos.
2. Desglose por estado — Conteos de Bids por BidStatus, Jobs por JobStatus, y montos de Invoices abiertos/pagados/total de pagos.
3. Panel de salud de la base de datos
| Field | Description |
|---|---|
| Connection | Si la BD del inquilino era alcanzable al momento de la solicitud (verde/rojo). |
| Database Size | Tamaño total de datos + índices desde information_schema (solo MySQL/MariaDB; muestra — en entornos SQLite). |
| Migrations Applied | Conteo de filas en django_migrations en la BD del inquilino. |
| Migrations Unapplied | Migraciones de Django presentes en el código pero no aplicadas a esta BD. Una migración pendiente aquí indica un paso de despliegue omitido para este inquilino. |
| Row Counts | Conteos por tabla de las tablas principales (usuarios, Bids, Jobs, Invoices, pagos, Activity Log, fotos). |
| Last Activity | Marca de tiempo de la entrada más reciente de ActivityLog en la BD del inquilino — indica si el inquilino se usa activamente. |
4. Actividad reciente — Las últimas 25 entradas de ActivityLog de la BD del inquilino en orden cronológico inverso.
Botón "Open as Tenant" — Abre /dashboard/?tenant={slug} en una pestaña nueva. Con ese parámetro de consulta, la aplicación envía el slug en solicitudes API del inquilino como encabezado X-Tenant-Slug y como parámetro acting_tenant (este último es importante en desarrollo local cuando el proxy Next.js puede no reenviar encabezados personalizados a Django). El slug también se guarda para la sesión del navegador. Solo los Platform Admins pueden usar este mecanismo; se ignora para todos los demás. Es solo para soporte — no es suplantación de usuario Auth0.
Resolución de problemas con el panel¶
| Symptom | Dónde mirar |
|---|---|
| El inquilino reporta errores en todas las páginas | Salud de BD → indicador Connection y mensaje de error |
| Los usuarios ven datos obsoletos tras un despliegue | Salud de BD → Migraciones no aplicadas |
| El inquilino está inactivo inesperadamente | Salud de BD → Last Activity |
| El conteo de Bid/Job no coincide con el informe del inquilino | Desglose por estado — conteos en vivo desde la BD del inquilino |
| La BD del inquilino consume demasiado almacenamiento | Salud de BD → tamaño de BD + conteos por fila |
API Platform Admin¶
Todos los endpoints requieren autenticación Platform Admin.
| Method | Endpoint | Description |
|---|---|---|
GET |
/platform/tenants/ |
Listar todos los inquilinos activos |
POST |
/platform/tenants/ |
Provisionar un nuevo inquilino |
GET |
/platform/tenants/summary/ |
Todos los inquilinos con agregados y salud de BD |
GET |
/platform/tenants/{id}/ |
Detalle del inquilino con conteo de usuarios |
GET |
/platform/tenants/{id}/dashboard/ |
Carga completa de observabilidad (conteos, salud de BD, actividad reciente) |
DELETE |
/platform/tenants/{id}/ |
Eliminar inquilino de forma lógica |
GET |
/platform/tenants/{id}/users/ |
Listar usuarios del inquilino |
GET |
/platform/tenants/{id}/users/{user_id}/ |
Detalle de usuario |
PATCH |
/platform/tenants/{id}/users/{user_id}/ |
Actualizar rol o estado del usuario |
DELETE |
/platform/tenants/{id}/users/{user_id}/ |
Eliminar usuario de forma lógica |
GET |
/platform/audit-log/ |
Registro de auditoría a nivel plataforma |
Eliminar un inquilino de forma lógica¶
Los inquilinos nunca se eliminan de forma definitiva. Cuando debe desactivarse un inquilino:
- El Platform Admin envía
DELETE /platform/tenants/{id}/. - El registro del inquilino se marca
is_deleted = truecon marcadeleted_at. - Todas las solicitudes a
hardhatflow.com/{slug}/...devuelven 404 de inmediato. - La base de datos del inquilino no se elimina — se conserva para retención de datos y posible recuperación.
- La acción queda en el ActivityLog de plataforma bajo
admin_action.
La desactivación completa de un inquilino (incluida eliminación de base de datos) debe hacerse manualmente a nivel de infraestructura por un Platform Admin y requiere autorización explícita.
Eliminar de forma lógica un inquilino bloquea de inmediato a todos sus usuarios. Asegúrese de que sea intencional antes de continuar.