fix(dashboard): resolver bugs críticos de tiempo real en HITL — race conditions, In-Band Auth y multi-stream buffer
- AppShell: corregir condición de carrera REST/WS que perdía tokens de agent_stream_chunk
- init_state atómico + eliminación de doble fuente REST/WS para actualización en tiempo real
- conversation_ended e idempotencia de eventos en máquina de estados por conversación
- Seguridad: migrar JWT de query param a In-Band Auth (primer mensaje {action:auth}) con timeout 5s y cierre 1008
- Multi-stream buffer: reemplazar buffer plano por TTL LRU (200 entradas, 60s TTL) para evitar pisado de tokens entre agentes
- agent_stream_completed ya no borra buffer incondicionalmente — delega purge a la política LRU
- Timer: corregir display de 00:00 en estado PENDING con visualización inmediata + cleanup en stop()
- Tests: 8 tests multi-stream, tests In-Band Auth, tests idempotencia y máquina de estados, tests Timer
- Resultado: 86/86 tests pasan | TypeScript 0 errores
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { auth } from '@/services/auth';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Types
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
export type AuthStatus = 'loading' | 'authenticated' | 'anonymous' | 'expired';
|
||||
|
||||
const CHECK_INTERVAL_MS = 30_000;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
// Hook
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Reactive hook that exposes the current authentication status.
|
||||
*
|
||||
* - 'loading': initial state while verifying session on mount
|
||||
* - 'authenticated': valid session exists (token + expireDate valid)
|
||||
* - 'anonymous': no session stored
|
||||
* - 'expired': session exists but expireDate has passed
|
||||
*/
|
||||
export function useAuth() {
|
||||
const [status, setStatus] = useState<AuthStatus>('loading');
|
||||
|
||||
useEffect(() => {
|
||||
function checkAuth(): void {
|
||||
const session = auth.getSession();
|
||||
|
||||
if (!session) {
|
||||
setStatus('anonymous');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check expireDate
|
||||
const expireMs = new Date(session.expireDate).getTime();
|
||||
if (isNaN(expireMs) || Date.now() >= expireMs) {
|
||||
auth.logout();
|
||||
setStatus('expired');
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify token is still valid
|
||||
const token = auth.getToken();
|
||||
if (!token) {
|
||||
setStatus('expired');
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus('authenticated');
|
||||
}
|
||||
|
||||
// Initial check
|
||||
checkAuth();
|
||||
|
||||
// Periodic re-check every 30s to detect expiry
|
||||
const interval = setInterval(checkAuth, CHECK_INTERVAL_MS);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
status,
|
||||
isAuthenticated: status === 'authenticated',
|
||||
isLoading: status === 'loading',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user