ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
Universidad del Valle 3D 웹 플랫폼, 심리학 학생이 AI 가이드 가상 환자와 임상 면접 연습
Plataforma web 3D de la Universidad del Valle donde estudiantes de psicología practican la entrevista clínica con pacientes virtuales guiados por inteligencia artificial.
교육TypeScript · PLpgSQLGitHub @stevenD18skz온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Plataforma web 3D interactiva para simulación de escenarios psicológicos con pacientes virtuales controlados por IA. Trabajo de Grado - Ingeniería de Sistemas, Universidad del Valle.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Plataforma web 3D para la simulación de escenarios de práctica clínica orientada a estudiantes de psicología, con pacientes virtuales (NPC) conducidos por inteligencia artificial.
Trabajo de grado — Brayan Steven Narváez Valdés, Universidad del Valle.
| Entorno | URL | Rama |
|---|---|---|
| Producción | https://psysim.vercel.app | main |
| Staging (preview) | https://psysim-git-develop-brayan-steven-narvaez-valdezs-projects.vercel.app | develop |
| Previews de otras ramas | https://psysim-git-<rama>-brayan-steven-narvaez-valdezs-projects.vercel.app | cualquier |
Los despliegues de preview (incluido staging) están protegidos por Vercel Authentication: solo los miembros del equipo de Vercel pueden abrirlos. Ver Tests E2E contra staging.
| Capa | Tecnología |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack, proxy.ts) · React 19 · TypeScript |
| UI | Tailwind CSS 4 · shadcn/ui (Radix) · lucide-react |
| Estado | Zustand 5 (store por árbol de React, slices) |
| Validación | Zod 4 · React Hook Form |
| Backend / BaaS | Supabase: Auth, PostgreSQL con RLS |
| IA | Google Gemini vía Vercel AI SDK (ai + @ai-sdk/google) |
| Calidad | ESLint 9 · Prettier · Husky + lint-staged + commitlint |
| Pruebas | Vitest + Testing Library (unitarias) · Playwright (E2E) |
| Despliegue | Vercel (Node.js 24) |
Requisitos: Node.js 24 (ver .nvmrc) y pnpm 11 (corepack enable).
git clone https://github.com/stevenD18skz/psysim.git
cd psysim
pnpm install # instala dependencias y activa los git hooks
cp .env.example .env.local # o: vercel link && vercel env pull .env.local
pnpm dev # http://localhost:3000
Plantilla completa en .env.example. .env.local nunca se versiona.
| Variable | Dónde | Descripción |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL | cliente/servidor | URL del proyecto de Supabase |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | cliente/servidor | Clave publicable sb_publishable_… (antes ANON_KEY) |
SUPABASE_SECRET_KEY | solo servidor | Clave secreta sb_secret_… (antes SERVICE_ROLE_KEY). Omite RLS |
AI_API_KEY | solo servidor | API key de Google Gemini |
AI_MODEL | solo servidor | Modelo de Gemini (por defecto gemini-3.5-flash) |
Las variables se validan con Zod al arrancar (src/lib/env): si falta alguna, el error indica cuál.
El esquema se versiona como migraciones SQL en supabase/migrations.
export SUPABASE_ACCESS_TOKEN=sbp_... # token personal de Supabase
pnpm exec supabase link --project-ref afqofsneoblsjjdvktsu
pnpm exec supabase db push # aplica migraciones pendientes
pnpm db:types # regenera src/types/database.types.ts
pnpm db:seed # crea las cuentas de prueba (idempotente)
pnpm db:seed crea tres docentes de prueba (docente{1,2,3}@psysim.test) y una cuenta sin rol
([email protected]) para probar el acceso denegado. Las contraseñas se generan aleatoriamente y
se muestran una sola vez (pnpm db:seed --reset las regenera).
Configuración de Auth aplicada al proyecto (también reflejada en supabase/config.toml):
public.custom_access_token_hook activado (añade el rol al JWT).| Parámetro | Valor |
|---|---|
| Proveedor | Google Gemini (Generative Language API) vía Vercel AI SDK 7 |
| URL base | https://generativelanguage.googleapis.com/v1beta |
| Modelo principal | gemini-3.5-flash (AI_MODEL), razonamiento minimal |
| Modelo respaldo | gemini-3.5-flash-lite (AI_MODEL_RESPALDO) |
| Límites | 12 s para el principal, 25 s en total, maxDuration de 30 s |
Se eligió gemini-3.5-flash por latencia (≈1 s por respuesta corta frente a ≈3–7 s de
gemini-3.8-flash), clave para un diálogo fluido con el paciente virtual. Como la latencia del
proveedor varía (el 29/09/2026 gemini-3.5-flash llegó a 11–16 s y a devolver 503), el Route
Handler pasa automáticamente al modelo de respaldo si el principal no responde en 12 s o está
saturado. Verificar la conexión:
pnpm ai:test
Route Handler POST /api/npc/chat (HU-12): recibe sesion_id, npc_id, mensaje_usuario e
historial; verifica la sesión del estudiante (401/403), valida con Zod (400), carga la sesión con
RLS (404 si no es del estudiante, 409 si ya finalizó o no ha comenzado), reserva un turno del
límite de 10 mensajes por minuto (429 con Retry-After; función reservar_peticion_ia), llama a
la IA con el prompt de la sesión (el que configuró el docente al asignar el caso) y guarda el
intercambio en la tabla mensaje. Responde respuesta_npc, timestamp_respuesta,
tokens_entrada y tokens_salida. Los errores de la IA se traducen a 504 (tiempo agotado), 503
(saturación) o 502, sin exponer detalles.
| Script | Descripción |
|---|---|
pnpm dev | Servidor de desarrollo (Turbopack) |
pnpm build / start | Build de producción / servidor de producción |
pnpm lint / lint:fix | ESLint (cero advertencias permitidas) |
pnpm typecheck | Verificación de tipos con tsc |
pnpm format / format:check | Prettier |
pnpm test | Tests unitarios (Vitest) |
pnpm test:coverage | Tests unitarios con cobertura |
pnpm test:e2e | Tests E2E (Playwright). Levanta pnpm dev si hace falta |
pnpm audit:deps | Vulnerabilidades conocidas de las dependencias (pnpm audit) |
pnpm check | lint + typecheck + formato + tests unitarios |
pnpm db:types | Regenera los tipos de la base de datos |
pnpm db:seed | Crea las cuentas semilla |
pnpm modelos:inspeccionar | Informe de los GLB de public/models (medidas, animaciones) |
pnpm modelos:subir | Optimiza los GLB con Draco y los sube a Supabase Storage |
pnpm supabase:test | Verifica conexión, RLS y claims del JWT con Supabase |
pnpm ai:test | Verifica la conexión con la API de IA |
src/
├── app/
│ ├── (auth)/login/ ← Login del docente (formulario + Server Action)
│ ├── (protected)/ ← Rutas que exigen sesión de docente
│ │ ├── layout.tsx ← Verificación autoritativa (DAL) + carga del perfil en Zustand
│ │ ├── configuracion/ ← Escenario o caso propio, paciente virtual y datos del estudiante; /casos crea y edita casos
│ │ ├── resultados/ ← Resultados de una práctica terminada: indicadores e historial (HU-21)
│ │ └── simulacion/ ← Simulación 3D de la sesión en curso (HU-08/09/10)
│ ├── api/npc/chat/ ← Route Handler del paciente virtual (HU-12)
│ ├── api/metrics/save/ ← Route Handler que guarda las métricas y cierra la sesión (HU-19)
│ ├── acceso-denegado/ ← Página para cuentas sin rol docente
│ ├── dev/ ← Herramientas solo de desarrollo (galería de modelos, escenas)
│ └── page.tsx ← Página pública de presentación
├── components/
│ ├── 3d/ ← React Three Fiber: SceneCanvas, SceneLoader, sala, muebles, NPC
│ ├── casos/ ← Constructor guiado de casos, vista previa del prompt y prueba del paciente
│ ├── configuracion/ ← Configurador de la sesión y "Mis casos"
│ ├── simulacion/ ← Simulación: HUD, panel de conversación, errores y cierre
│ ├── layout/ ← Navegación principal, botón de logout
│ └── ui/ ← Componentes de shadcn/ui
├── lib/
│ ├── auth/ ← Rutas, DAL (verificación de sesión/rol), Server Actions
│ ├── conversacion/ ← Máquina de estados del NPC, envío de mensajes y guardado de métricas (HU-13/14/16/20)
│ ├── metrics.ts ← Indicadores agregados de la sesión (HU-18)
│ ├── contraste.ts ← Razón de contraste WCAG para validar el tema (HU-25)
│ ├── escena/ ← Colisiones, encuadre, animaciones, ajuste de modelos, Storage
│ ├── ia/ ← Llamada al modelo de lenguaje con respaldo y clasificación de errores
│ ├── escenarios/ ← Consultas y Server Actions de escenarios y sesiones
│ ├── casos/ ← Casos propios del docente: composición del prompt, opciones y Server Actions
│ ├── env/ ← Validación de variables de entorno
│ └── supabase/ ← Clientes: browser, server, admin (clave secreta), proxy
├── schemas/ ← Esquemas Zod compartidos cliente/servidor
├── store/ ← Store de Zustand (slices)
├── types/ ← Tipos globales y tipos generados de la BD
└── proxy.ts ← Proxy de Next.js (antes middleware)
public/models/ ← Modelos 3D fuente (se publican optimizados en Supabase Storage)
public/scenes/ ← JSON de la escena 3D de cada escenario (docs/modelos-3d.md)
public/draco/ ← Decodificadores Draco autoalojados
supabase/ ← config.toml y migraciones SQL
scripts/ ← Scripts de verificación y seed
e2e/ ← Tests de Playwright
docs/ ← Documentación y registros de pruebas
src/proxy.ts): en cada petición refresca la sesión y verifica la firma del JWT
con getClaims() (claves asimétricas, sin llamada de red). En /configuracion y
/simulacion redirige a /login sin sesión, o a /acceso-denegado si el claim
rol_usuario del JWT no es docente.src/lib/auth/dal.ts): el layout y cada página protegida validan la
sesión con el servidor de Auth (getUser(), detecta sesiones revocadas) y leen el rol de la
tabla usuario. Es la verificación autoritativa.estado y fin.El rol llega al JWT mediante un Custom Access Token Hook de Supabase, lo que evita consultar la base de datos en cada petición del proxy.
La simulación monta la escena descrita por public/scenes/e-XX.json (validado con Zod): una sala
procedural cálida, mobiliario (procedural o GLB desde Supabase Storage, comprimido con Draco),
el paciente virtual y controles en primera persona (WASD + ratón con Pointer Lock) con colisiones
por cajas delimitadoras. El canvas limita el devicePixelRatio a 1,5 y lo baja a 1 si el
rendimiento cae (PerformanceMonitor). Añade ?debug=1 a la URL de la simulación para ver los FPS.
Guía de modelos y escenas: docs/modelos-3d.md.
Al acercarse al paciente (≤ 3,2 m) el estudiante inicia la conversación con la tecla E, un clic sobre él o el botón "Conversar con …". La cámara se desplaza con suavidad hasta quedar frente al paciente y aparece el panel de conversación (Enter envía, Shift + Enter inserta una línea).
El estado del paciente sigue la máquina de estados de la sección 4.3.4
(src/lib/conversacion/estados-npc.ts): inactivo → esperando_input → procesando → respondiendo → esperando_input, con error_comunicacion (Reintentar / Finalizar sesión) y sesion_finalizada.
Las transiciones no válidas se rechazan (p. ej. un doble envío). El paciente reacciona a cada
estado: sigue al estudiante con la mirada, baja la cabeza al reflexionar y asiente y mueve la boca
al responder; con un GLB riggeado usa sus clips Idle, Pensando y Hablando con fundidos de
0,3 s. La conversación se guarda en la tabla mensaje y se recupera al recargar la página.
| Rama | Propósito | Despliegue |
|---|---|---|
main | Código estable en producción | Producción (Vercel) |
develop | Integración continua de funcionalidades | Staging (preview) |
feature/* | Nuevas funcionalidades (desde develop) | Preview |
fix/* | Corrección de errores | Preview |
hotfix/* | Correcciones urgentes sobre main | Preview |
docs/* | Documentación | Preview |
refactor/* | Refactorización sin cambios funcionales | Preview |
Flujo: feature/* → PR a develop (staging) → PR de develop a main (producción).
Se usa Conventional Commits, validado por
commitlint en el hook commit-msg:
feat(auth): implementar login con Supabase
fix(proxy): conservar cookies al redirigir
docs(readme): documentar variables de entorno
Tipos: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.
lint-staged (ESLint --fix + Prettier sobre los archivos preparados) y
pnpm typecheck. El commit se rechaza si algo falla.commitlint.pnpm test # unitarias (Vitest)
pnpm test:coverage # con cobertura: falla si app/api, lib o store bajan del 70 %
pnpm test:e2e:install # (una vez) instala Chromium para Playwright
pnpm test:e2e # E2E contra http://localhost:3000
pnpm test:e2e e2e/accesibilidad.spec.ts # un solo archivo
Los tests están separados por funcionalidad (un archivo por historia o módulo), no en una suite única:
| Funcionalidad | Unitarias | E2E (Playwright) |
|---|---|---|
| Acceso y rutas protegidas (HU-02–04) | lib/auth/*.test.ts, proxy.test.ts | autenticacion.spec.ts |
| Configuración y casos (HU-06/07) | lib/casos/*.test.ts, components/configuracion/*.test.tsx | configuracion.spec.ts |
| Conversación con el paciente (HU-12) | app/api/npc/chat/route.test.ts, lib/conversacion/*.test.ts | conversacion.spec.ts |
| Estado global (HU-14) | store/*.test.ts | — |
| HUD y cierre de sesión (HU-17, 20) | components/simulacion/hud-sesion.test.tsx, use-finalizar-sesion.test.tsx | flujo-completo.spec.ts |
| Métricas (HU-18, 19) | lib/metrics.test.ts, schemas/metrics.schema.test.ts, app/api/metrics/save/route.test.ts | resultados.spec.ts |
| Resultados (HU-21) | lib/sesiones/resultados.test.ts, components/resultados/*.test.tsx | resultados.spec.ts |
| Navegación (HU-22) | — | navegacion.spec.ts |
| Instrucciones del caso (HU-23) | components/simulacion/instrucciones-caso.test.tsx | flujos.ts (paso común) |
| Escenarios y prompts (HU-24) | lib/escenarios/prompts-escenarios.test.ts, schemas/escena.schema.test.ts | laboratorio.spec.ts |
| Accesibilidad (HU-25) | lib/contraste.test.ts (contraste WCAG AA del tema) | accesibilidad.spec.ts (axe-core, teclado) |
| Rendimiento 3D y web (HU-28) | lib/rendimiento.test.ts (presupuestos de peso) | rendimiento.spec.ts, rendimiento-web.spec.ts |
| Despliegue y seguridad (HU-29/30) | lib/seguridad.test.ts, lib/seguridad/csp.test.ts, , , |
Los tests E2E autenticados usan las credenciales de .env.test.local (ver .env.example:
E2E_DOCENTE_*, E2E_ESTUDIANTE_*, E2E_ADMIN_*, E2E_SIN_ROL_*); si no existen, esos tests
se omiten. Registros de pruebas por sprint en docs/pruebas/.
PLAYWRIGHT_BASE_URL=https://psysim-git-develop-brayan-steven-narvaez-valdezs-projects.vercel.app \
VERCEL_AUTOMATION_BYPASS_SECRET=<secreto> \
pnpm test:e2e e2e/smoke.spec.ts
.env* está ignorado por Git (excepto .env.example).SUPABASE_SECRET_KEY y AI_API_KEY solo se usan en el servidor (import 'server-only').next.config.ts (X-Frame-Options, nosniff,
Referrer-Policy, Permissions-Policy); HSTS lo añade Vercel.no-store para que ninguna CDN las
almacene.src/proxy.ts),
HSTS y Cross-Origin-Opener-Policy; el paciente de IA arma su contexto en el servidor y descarta
respuestas que copian sus instrucciones. Detalle en docs/auditoria-seguridad.md.pg_graphql desactivada: la aplicación solo usa la API REST.pnpm test incluye un checklist de seguridad automatizado (src/lib/seguridad.test.ts): RLS en
todas las tablas, funciones security definer con search_path fijo, ningún permiso al rol
anónimo, secretos solo en el servidor y encabezados de seguridad.lib/ia/fuga-prompt.test.tslib/ia/historial.test.tslib/env/entorno-documentado.test.tssmoke.spec.ts, csp.spec.ts |