Control de Acceso
Endpoints para gestionar el control de acceso físico al gimnasio usando dispositivos ControlID IdFace. El sistema permite enrolar usuarios (rostro + PIN/cédula) y validar el acceso en tiempo real según el tipo de membresía.
Admin / Staff Los endpoints de administración están restringidos a administradores y staff.
Arquitectura
Section titled “Arquitectura”El sistema opera con 2 dispositivos en modo Online:
| Dispositivo | Nombre | Política de Acceso |
|---|---|---|
Talanquera (turnstile) | Entrada principal | Membresía ACTIVE (cualquier tipo) |
Zona Premium (premium) | Área exclusiva | Membresía ACTIVE + tipo PREMIUM/VIP/ENTERPRISE |
Los usuarios se enrolan en ambos dispositivos siempre. La validación de acceso ocurre en tiempo real cuando el usuario se identifica (rostro o PIN).
Endpoints de Administración
Section titled “Endpoints de Administración”Enrolar Usuario
Section titled “Enrolar Usuario”Registra un usuario en ambos dispositivos (crea usuario + asigna PIN con su cédula).
- URL:
/api/v1/access-control/enroll/:userProfileId - Método:
POST - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”userProfileId: ID del perfil de usuario a enrolar
Respuesta
Section titled “Respuesta”{ "message": "Usuario enrolado exitosamente", "data": { "userProfileId": "clx1234567890", "name": "Juan Pérez", "controlIdUserId": 1234567890, "enrolledDevices": ["turnstile", "premium"] }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].enroll[':userProfileId'].$post({ param: { userProfileId: 'clx1234567890' }})Captura Facial
Section titled “Captura Facial”Inicia la captura facial en el dispositivo Talanquera vía remote_enroll. El usuario debe estar parado frente al dispositivo. Una vez capturada, la imagen se copia automáticamente al dispositivo Premium.
- URL:
/api/v1/access-control/enroll/:userProfileId/face - Método:
POST - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”userProfileId: ID del perfil de usuario
Respuesta
Section titled “Respuesta”{ "message": "Rostro registrado exitosamente", "data": { "userProfileId": "clx1234567890", "faceEnrolled": true, "syncedTo": ["turnstile", "premium"] }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].enroll[':userProfileId'].face.$post({ param: { userProfileId: 'clx1234567890' }})Eliminar Usuario de Dispositivos
Section titled “Eliminar Usuario de Dispositivos”Elimina un usuario de todos los dispositivos (usuario, PIN e imagen facial).
- URL:
/api/v1/access-control/enroll/:userProfileId - Método:
DELETE - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”userProfileId: ID del perfil de usuario
Respuesta
Section titled “Respuesta”{ "message": "Usuario removido de los dispositivos", "data": { "removedFrom": ["turnstile", "premium"] }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].enroll[':userProfileId'].$delete({ param: { userProfileId: 'clx1234567890' }})Sincronizar Todos los Usuarios
Section titled “Sincronizar Todos los Usuarios”Sincronización masiva: enrola todos los usuarios con cédula registrada en ambos dispositivos. Útil para setup inicial o después de un reset del dispositivo.
- URL:
/api/v1/access-control/sync - Método:
POST - Autenticación Requerida: Sí (Admin)
Respuesta
Section titled “Respuesta”{ "message": "Sincronización completada", "data": { "enrolled": 45, "errors": 2 }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].sync.$post()Estado del Dispositivo
Section titled “Estado del Dispositivo”Consulta si un dispositivo está online y obtiene información del sistema.
- URL:
/api/v1/access-control/devices/:deviceType/status - Método:
GET - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”deviceType:turnstileopremium
Respuesta
Section titled “Respuesta”{ "data": { "deviceType": "turnstile", "online": true, "info": { "serial": "ABC123", "firmware": "4.10.0", "model": "iDFace" } }}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].devices[':deviceType'].status.$get({ param: { deviceType: 'turnstile' }})Logs de Acceso
Section titled “Logs de Acceso”Obtiene los logs de acceso del dispositivo (eventos de entrada/salida).
- URL:
/api/v1/access-control/devices/:deviceType/logs - Método:
GET - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”deviceType:turnstileopremium
Respuesta
Section titled “Respuesta”{ "data": [ { "id": 1, "time": 1698400000, "event": 7, "user_id": 1234567890, "portal_id": 1 } ]}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].devices[':deviceType'].logs.$get({ param: { deviceType: 'turnstile' }})Listar Usuarios Enrolados
Section titled “Listar Usuarios Enrolados”Lista todos los usuarios registrados en un dispositivo.
- URL:
/api/v1/access-control/devices/:deviceType/users - Método:
GET - Autenticación Requerida: Sí (Admin/Staff)
Parámetros de Ruta
Section titled “Parámetros de Ruta”deviceType:turnstileopremium
Respuesta
Section titled “Respuesta”{ "data": [ { "id": 1234567890, "name": "Juan Pérez", "registration": "1234567890" } ]}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].devices[':deviceType'].users.$get({ param: { deviceType: 'premium' }})Configurar Dispositivo (Modo Online)
Section titled “Configurar Dispositivo (Modo Online)”Configura un dispositivo para operar en modo online. El dispositivo comenzará a enviar eventos de acceso al webhook del API para validación en tiempo real.
- URL:
/api/v1/access-control/devices/:deviceType/configure - Método:
POST - Autenticación Requerida: Sí (Admin)
Parámetros de Ruta
Section titled “Parámetros de Ruta”deviceType:turnstileopremium
Respuesta
Section titled “Respuesta”{ "message": "Dispositivo \"turnstile\" configurado en modo online"}Ejemplo de Cliente Hono
Section titled “Ejemplo de Cliente Hono”import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1['access-control'].devices[':deviceType'].configure.$post({ param: { deviceType: 'turnstile' }})Endpoints del Monitor (Webhooks)
Section titled “Endpoints del Monitor (Webhooks)”Estos endpoints no requieren autenticación. Son llamados directamente por los dispositivos ControlID cuando ocurre un evento de acceso.
Evento de Acceso (DAO)
Section titled “Evento de Acceso (DAO)”Endpoint principal del monitor. Recibe un evento de identificación del dispositivo, valida la membresía del usuario en tiempo real, y responde con la decisión de acceso.
- URL:
/api/v1/access-control/monitor/dao - Método:
POST - Autenticación Requerida: No (llamado por el dispositivo)
Cuerpo de la Petición (enviado por el dispositivo)
Section titled “Cuerpo de la Petición (enviado por el dispositivo)”{ "user_id": 1234567890, "event": 1}Respuesta
Section titled “Respuesta”{ "response": 1, "message": "Acceso permitido"}Valor de response | Significado |
|---|---|
1 | Acceso permitido |
0 | Acceso denegado |
Heartbeat del Dispositivo
Section titled “Heartbeat del Dispositivo”Endpoint de keepalive para monitorear que los dispositivos están activos.
- URL:
/api/v1/access-control/monitor/device_is_alive - Método:
POST - Autenticación Requerida: No (llamado por el dispositivo)
Respuesta
Section titled “Respuesta”{ "ok": true}Flujo de Enrolamiento
Section titled “Flujo de Enrolamiento”- Crear usuario + PIN:
POST /enroll/:userProfileId→ Registra en ambos dispositivos - Captura facial:
POST /enroll/:userProfileId/face→ El usuario se para frente a la Talanquera → La imagen se copia automáticamente al dispositivo Premium
Flujo de Acceso (Tiempo Real)
Section titled “Flujo de Acceso (Tiempo Real)”- El usuario se identifica en un dispositivo (rostro o PIN)
- El dispositivo envía un evento a
POST /monitor/dao - La API verifica la membresía del usuario en la base de datos
- La API responde con
1(permitido) o0(denegado) - El dispositivo ejecuta la acción correspondiente
Variables de Entorno
Section titled “Variables de Entorno”| Variable | Descripción | Ejemplo |
|---|---|---|
CONTROLID_TURNSTILE_IP | IP del dispositivo Talanquera (vía Tailscale/RPI) | 100.x.x.x:80 |
CONTROLID_TURNSTILE_LOGIN | Login del dispositivo Talanquera | admin |
CONTROLID_TURNSTILE_PASSWORD | Password del dispositivo Talanquera | admin |
CONTROLID_PREMIUM_IP | IP del dispositivo Premium (vía Tailscale/RPI) | 100.x.x.x:81 |
CONTROLID_PREMIUM_LOGIN | Login del dispositivo Premium | admin |
CONTROLID_PREMIUM_PASSWORD | Password del dispositivo Premium | admin |
CONTROLID_API_CALLBACK_URL | URL del API para el Monitor (alcanzable desde los dispositivos) | http://100.x.x.x:3000 |