/** * Composable para manejo de sesiones de catación en IndexedDB * Maneja solo una sesión activa a la vez */ import type { SesionCatacion } from '~/types/catacion' const DB_NAME = 'RioCataDB' const DB_VERSION = 1 const STORE_NAME = 'sesiones' const ACTIVE_SESSION_KEY = 'sesion-activa' /** * Inicializa y retorna la base de datos IndexedDB */ function openDatabase(): Promise { return new Promise((resolve, reject) => { // Verificar si estamos en el cliente if (typeof window === 'undefined' || !window.indexedDB) { reject(new Error('IndexedDB no está disponible en este entorno')) return } const request = window.indexedDB.open(DB_NAME, DB_VERSION) request.onerror = () => { reject(new Error('Error al abrir la base de datos')) } request.onsuccess = () => { resolve(request.result) } request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result // Crear object store si no existe if (!db.objectStoreNames.contains(STORE_NAME)) { const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'sessionId' }) // Crear índice por fecha para consultas futuras objectStore.createIndex('fecha', 'fecha', { unique: false }) } } }) } /** * Guarda o actualiza la sesión activa en IndexedDB */ async function saveSession(sesion: SesionCatacion): Promise { try { const db = await openDatabase() const transaction = db.transaction([STORE_NAME], 'readwrite') const objectStore = transaction.objectStore(STORE_NAME) // Eliminar todas las sesiones anteriores await new Promise((resolve, reject) => { const clearRequest = objectStore.clear() clearRequest.onsuccess = () => resolve() clearRequest.onerror = () => reject(new Error('Error al limpiar sesiones anteriores')) }) // Guardar la nueva sesión await new Promise((resolve, reject) => { const addRequest = objectStore.add(sesion) addRequest.onsuccess = () => resolve() addRequest.onerror = () => reject(new Error('Error al guardar la sesión')) }) db.close() } catch (error) { console.error('Error en saveSession:', error) throw error } } /** * Carga la sesión activa desde IndexedDB */ async function loadSession(): Promise { try { const db = await openDatabase() const transaction = db.transaction([STORE_NAME], 'readonly') const objectStore = transaction.objectStore(STORE_NAME) const sesion = await new Promise((resolve, reject) => { // Obtener todas las claves const getAllRequest = objectStore.getAll() getAllRequest.onsuccess = () => { const sesiones = getAllRequest.result as SesionCatacion[] // Retornar la primera sesión (debería haber solo una) const sesion = sesiones.length > 0 ? sesiones[0] : null resolve(sesion || null) } getAllRequest.onerror = () => { reject(new Error('Error al cargar la sesión')) } }) db.close() return sesion } catch (error) { console.error('Error en loadSession:', error) return null } } /** * Verifica si existe una sesión activa */ async function hasActiveSession(): Promise { try { const sesion = await loadSession() return sesion !== null } catch (error) { console.error('Error en hasActiveSession:', error) return false } } /** * Elimina la sesión activa */ async function deleteSession(): Promise { try { const db = await openDatabase() const transaction = db.transaction([STORE_NAME], 'readwrite') const objectStore = transaction.objectStore(STORE_NAME) await new Promise((resolve, reject) => { const clearRequest = objectStore.clear() clearRequest.onsuccess = () => resolve() clearRequest.onerror = () => reject(new Error('Error al eliminar la sesión')) }) db.close() } catch (error) { console.error('Error en deleteSession:', error) throw error } } /** * Actualiza una sesión existente (timestamp de modificación) */ async function updateSession(sesion: SesionCatacion): Promise { try { // Actualizar timestamp de modificación sesion.modificadoEn = Date.now() const db = await openDatabase() const transaction = db.transaction([STORE_NAME], 'readwrite') const objectStore = transaction.objectStore(STORE_NAME) await new Promise((resolve, reject) => { const putRequest = objectStore.put(sesion) putRequest.onsuccess = () => resolve() putRequest.onerror = () => reject(new Error('Error al actualizar la sesión')) }) db.close() } catch (error) { console.error('Error en updateSession:', error) throw error } } /** * Composable principal para manejo de IndexedDB */ export const useIndexedDB = () => { // Estado reactivo de la sesión const sesionActiva = useState('sesion-activa', () => null) const cargando = useState('sesion-cargando', () => false) const error = useState('sesion-error', () => null) /** * Inicializa el composable cargando la sesión activa */ const inicializar = async () => { if (import.meta.server) { // No hacer nada en el servidor return } try { cargando.value = true error.value = null const sesion = await loadSession() sesionActiva.value = sesion } catch (err) { error.value = err as Error console.error('Error al inicializar IndexedDB:', err) } finally { cargando.value = false } } /** * Guarda la sesión activa */ const guardar = async (sesion: SesionCatacion) => { if (import.meta.server) { console.warn('No se puede guardar en IndexedDB desde el servidor') return } try { cargando.value = true error.value = null await saveSession(sesion) sesionActiva.value = sesion } catch (err) { error.value = err as Error console.error('Error al guardar sesión:', err) throw err } finally { cargando.value = false } } /** * Actualiza la sesión activa */ const actualizar = async (sesion: SesionCatacion) => { if (import.meta.server) { console.warn('No se puede actualizar en IndexedDB desde el servidor') return } try { cargando.value = true error.value = null await updateSession(sesion) sesionActiva.value = sesion } catch (err) { error.value = err as Error console.error('Error al actualizar sesión:', err) throw err } finally { cargando.value = false } } /** * Elimina la sesión activa */ const eliminar = async () => { if (import.meta.server) { console.warn('No se puede eliminar de IndexedDB desde el servidor') return } try { cargando.value = true error.value = null await deleteSession() sesionActiva.value = null } catch (err) { error.value = err as Error console.error('Error al eliminar sesión:', err) throw err } finally { cargando.value = false } } /** * Verifica si hay una sesión activa */ const tieneSecion = computed(() => sesionActiva.value !== null) return { // Estado sesionActiva: readonly(sesionActiva), cargando: readonly(cargando), error: readonly(error), tieneSecion, // Métodos inicializar, guardar, actualizar, eliminar, } }