Saltar al contenido principal

Turnero, onboarding y configuración de plataforma

Tres piezas recientes que no tenían página técnica. El uso está en el manual de usuario, en Agenda → Turnero y Primeros pasos → Configuración guiada (otro sitio: no se enlaza desde aquí).

Turnero (kiosk)​

Módulo kiosk, depende de scheduling. Manifiesto en backend/src/modules/manifests.ts (kioskManifest), código en backend/src/modules/kiosk/ y frontend/src/sections/kiosk/. Esquema en 20260922000001 (estados arrived / in_progress de la cita), 20260922000002 (tablas) y 20260922000003 (vinculación).

TablaQué guardaAcceso
kiosk_devicesMáquinas: tipo checkin / display, alcance tenant / workspace / sede, hash del tokenCerrada a anon y authenticated; solo backend
kiosk_ticket_sequencesContador de turnos por sede y día local de la sedeSolo backend
kiosk_ticketsTurnos emitidosLectura por RLS (la cola), escritura por backend
  • Sin cuentas en la máquina. El kiosko autentica con Authorization: Kiosk <token>; en base solo está su sha256, y revocar es poner el hash a NULL. KioskGuard es la única barrera entre un vestíbulo público y la agenda.
  • Vinculación por código en pantalla (patrón RFC 8628): la máquina muestra un código, el panel lo reclama (POST /api/kiosk/admin/devices/claim, permiso kiosk.devices.manage) y la máquina recoge su credencial en el siguiente sondeo.
  • Numeración atómica con kiosk_issue_ticket (SECURITY DEFINER, solo service_role).
  • Privacidad por defecto: búsqueda con respuesta idéntica para documento desconocido y sin cita, límite de intentos con bloqueo (KioskAttemptLimiter), y nada de servicio, profesional ni nombre en papel o TV salvo que la política (tenant_modules.config del módulo) lo active.
  • El turno nunca escribe el estado de la cita por su cuenta: pasa por SchedulingService.updateStatus, dueño único de las transiciones y sus efectos.
  • Instalador: frontend/public/instalar-kiosko.ps1 configura Chrome (--kiosk, --kiosk-printing, perfil propio en %LOCALAPPDATA%\XenpiaKiosk) y crea el acceso directo de arranque, que abre /kiosk?instalador=1. Se ejecuta de dos formas: con el .cmd que descarga la propia pantalla del kiosko (frontend/src/sections/kiosk/installer.ts, generado en el navegador con el origen de la página) o con la línea de PowerShell del panel.
  • ¿Se abrió con el instalador? La pantalla guarda la marca instalador=1 en sessionStorage (la vinculación recarga /kiosk sin ella) y la envía en POST /api/kiosk/pair/start (via_installer). Al recoger la credencial se copia a kiosk_devices.via_installer, y el panel avisa al vincular y marca la máquina como «Sin instalador». Es una señal que declara la máquina: informativa, nunca autoriza nada. NULL = vinculada antes de existir la señal.
  • En Windows, sin la marca, la pantalla ofrece descargar el instalador antes de pedir código: la credencial vive en el localStorage del perfil de Chrome, así que vincular desde un Chrome cualquiera y después instalar obliga a vincular dos veces.

Onboarding (/api/onboarding)​

Onboarding en 3 pasos (ADR 0007): negocio → asistente → WhatsApp. Backend en backend/src/onboarding/, pantalla a pantalla completa en /onboarding (frontend/src/sections/onboarding/; /dashboard/onboarding redirige). Migraciones 20260926000001 (tablas) y 20261022000003 (paso a 3 pasos).

  • industry_packs: catálogo global de sectores. Su payload (tipado en shared/src/onboarding/types.ts) define módulos, roles, servicios, horarios y la plantilla del bot (prompt_template, flow_template opcional, compliance).
  • tenant_onboarding: estado por tenant (pending, in_progress, completed, skipped), las respuestas de cada paso (answers.business, answers.assistant, answers.channel) y los pasos hechos, para retomar sin perder datos.
  • Rutas:
RutaQué hace
POST businessAplica el pack (módulos, roles, servicios, recordatorios), renombra el tenant, fija zona horaria y país, y crea la sede principal si hay agenda. Frena lo evidente con los patrones del guardián.
POST prompt-draftPrompt prediseñado (buildAssistantPrompt); con improve: true, reescritura con IA (6/min).
POST preview-chatChat de prueba con el prompt en borrador + reglas de plataforma, sin herramientas (20/min, 150/día por tenant).
POST assistantArma el flujo (buildSectorFlow), lo pasa por el guardián y crea o actualiza el bot con su versión de flujo. blocked no guarda el bot.
POST channelCrea la cuenta de canal, enciende WhatsApp y la ata al bot (20 msg/min si el tenant es nuevo). webchat solo en sandbox.
POST complete / skipCierra el onboarding (con el modo y el número conectados).
  • El Embedded Signup del paso 3 lo hace el frontend con captureEmbeddedSignup + completeWhatsAppOnboarding (ADR 0006) sobre la cuenta que devuelve POST channel.
  • OnboardingGate (layout del dashboard) lleva a /onboarding a quien tiene pending o in_progress; "Hacerlo después" lo desactiva por la sesión y la tarjeta del inicio permite retomarlo.
  • Autorización: lecturas con pertenencia al tenant; escribir pasos, completar u omitir exige rol admin/administrador/owner o core.modules.manage / bot.create, porque el admin recién creado nace con permisos incompletos. POST /api/onboarding/preseed exige superadmin y lo usa el alta de tenant (deja el sector elegido y el estado en pending).
  • Preferencias de recorridos y ayudas por usuario (GET|POST /api/onboarding/user-state).

Guardián de seguridad de los bots (/api/bot-safety)​

Detalle y motivos en el ADR 0007. En corto:

  • backend/src/bot-safety/: patrones (safety-patterns.ts), combinación de capas (safety-verdict.ts), capas de OpenAI (openai-safety-layers.ts) y BotSafetyService.
  • bots.safety_status (approved | pending | review | blocked) + safety_hash, y el historial en bot_safety_reviews (solo service-role). Migración 20261022000001.
  • El registro de bots (BotsRegistry) llama a shouldServe antes de cargar cada bot.
  • BOT_SAFETY_MODE: observe (por defecto) o enforce.
  • Superadmin: GET queue, POST :botId/decision (approved / blocked) y POST tenants/:tenantId/verify (quita los límites de cuenta nueva). El tenant puede pedir POST :botId/review tras editar su bot.

Límites de cuenta nueva​

tenants.trust_level (new | verified, migración 20261022000002). TenantTrustService (backend/src/common/tenant-trust/) se consulta en ChannelDispatcherRegistry.dispatch: un tenant new tiene tope diario de mensajes y de plantillas, y pasa solo a verified tras NEW_TENANT_TRUST_DAYS. Un trigger impide que el panel cambie su propio nivel.

Configuración de plataforma (/api/admin/platform-settings)​

Interruptores globales de la plataforma, no por tenant. Backend en backend/src/platform-settings/, tabla platform_settings (20260924000001), pantalla en /admin/settings/platform.

  • GET lista y PUT /:key escribe; ambos detrás de SuperadminGuard.
  • PlatformSettingsService.get(key, fallback) cachea 30 s y es fail-closed: si la fila falta o la consulta falla devuelve el fallback de quien llama. Tras un PUT se invalida la caché.
  • Claves actuales: copilot.write_actions (el copiloto puede ejecutar cambios, solo superadmin), flow_assistant.enabled (asistente del editor de flujos; nace apagado) y product_tours.enabled (tours, hints y centro de ayuda del dashboard; nace encendido).
  • La lectura RLS está abierta a authenticated: no guardar secretos aquí.