252 lines
6.2 KiB
TypeScript
252 lines
6.2 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { useRequestFetch } from '#imports'
|
|
|
|
export interface TableMetadata {
|
|
table: string
|
|
rowCount: number
|
|
primaryKey: string
|
|
approxSizeBytes: number
|
|
columns: string[]
|
|
createdAtRange?: {
|
|
from: string
|
|
to: string
|
|
}
|
|
lastRefreshed?: string
|
|
sampleRow?: Record<string, unknown>
|
|
}
|
|
|
|
export interface MetadataState {
|
|
metadata: TableMetadata[]
|
|
loading: boolean
|
|
error: string | null
|
|
lastUpdated: string | null
|
|
initialized: boolean
|
|
}
|
|
|
|
export const useMetadataStore = defineStore('metadata', {
|
|
state: (): MetadataState => ({
|
|
metadata: [],
|
|
loading: false,
|
|
error: null,
|
|
lastUpdated: null,
|
|
initialized: false
|
|
}),
|
|
|
|
getters: {
|
|
/**
|
|
* Get metadata for all tables
|
|
*/
|
|
allTables: (state): TableMetadata[] => state.metadata,
|
|
|
|
/**
|
|
* Get metadata for a specific table
|
|
*/
|
|
getTableMetadata: (state) => (tableName: string): TableMetadata | undefined => {
|
|
return state.metadata.find(meta => meta.table === tableName)
|
|
},
|
|
|
|
/**
|
|
* Get total number of tables
|
|
*/
|
|
totalTables: (state): number => state.metadata.length,
|
|
|
|
/**
|
|
* Get total number of records across all tables
|
|
*/
|
|
totalRecords: (state): number => {
|
|
return state.metadata.reduce((sum, meta) => sum + (meta.rowCount || 0), 0)
|
|
},
|
|
|
|
/**
|
|
* Get list of all table names
|
|
*/
|
|
tableNames: (state): string[] => {
|
|
return state.metadata.map(meta => meta.table)
|
|
},
|
|
|
|
/**
|
|
* Check if metadata is available
|
|
*/
|
|
hasMetadata: (state): boolean => state.metadata.length > 0,
|
|
|
|
/**
|
|
* Check if metadata is currently loading
|
|
*/
|
|
isLoading: (state): boolean => state.loading,
|
|
|
|
/**
|
|
* Check if there's an error
|
|
*/
|
|
hasError: (state): boolean => !!state.error,
|
|
|
|
/**
|
|
* Get formatted last updated time
|
|
*/
|
|
formattedLastUpdated: (state): string => {
|
|
if (!state.lastUpdated) return 'Nunca'
|
|
|
|
try {
|
|
return new Date(state.lastUpdated).toLocaleString('es-ES', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})
|
|
} catch {
|
|
return 'Fecha inválida'
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if metadata is stale (older than 5 minutes)
|
|
*/
|
|
isStale: (state): boolean => {
|
|
if (!state.lastUpdated) return true
|
|
|
|
const lastUpdate = new Date(state.lastUpdated)
|
|
const now = new Date()
|
|
const fiveMinutes = 5 * 60 * 1000
|
|
|
|
return (now.getTime() - lastUpdate.getTime()) > fiveMinutes
|
|
}
|
|
},
|
|
|
|
actions: {
|
|
/**
|
|
* Load metadata lazily (only if not already loaded)
|
|
*/
|
|
async loadMetadata(force = false): Promise<void> {
|
|
// Don't load if already loading
|
|
if (this.loading) return
|
|
|
|
// Don't load if already initialized and not forced
|
|
if (this.initialized && !force && !this.isStale) return
|
|
|
|
await this.fetchMetadata()
|
|
},
|
|
|
|
/**
|
|
* Force refresh metadata
|
|
*/
|
|
async refreshMetadata(): Promise<void> {
|
|
await this.fetchMetadata()
|
|
},
|
|
|
|
/**
|
|
* Internal method to fetch metadata from API
|
|
*/
|
|
async fetchMetadata(): Promise<void> {
|
|
this.loading = true
|
|
this.error = null
|
|
|
|
try {
|
|
const requestFetch = useRequestFetch()
|
|
const response = await requestFetch('/api/metadata')
|
|
|
|
if (Array.isArray(response)) {
|
|
this.metadata = response.map((meta: any) => ({
|
|
...meta,
|
|
lastRefreshed: meta.lastRefreshed || new Date().toISOString()
|
|
}))
|
|
this.lastUpdated = new Date().toISOString()
|
|
this.initialized = true
|
|
|
|
// Persist to localStorage for offline access
|
|
if (process.client) {
|
|
try {
|
|
localStorage.setItem('metadata-cache', JSON.stringify({
|
|
metadata: this.metadata,
|
|
lastUpdated: this.lastUpdated
|
|
}))
|
|
} catch (error) {
|
|
console.warn('Failed to persist metadata to localStorage:', error)
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error('Invalid metadata response format')
|
|
}
|
|
} catch (error: any) {
|
|
this.error = this.extractErrorMessage(error)
|
|
console.error('Error fetching metadata:', error)
|
|
|
|
// Try to load from cache if available
|
|
if (process.client && !this.hasMetadata) {
|
|
this.loadFromCache()
|
|
}
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Load metadata from localStorage cache
|
|
*/
|
|
loadFromCache(): void {
|
|
if (!process.client) return
|
|
|
|
try {
|
|
const cached = localStorage.getItem('metadata-cache')
|
|
if (cached) {
|
|
const parsedCache = JSON.parse(cached)
|
|
this.metadata = parsedCache.metadata || []
|
|
this.lastUpdated = parsedCache.lastUpdated || null
|
|
this.initialized = true
|
|
}
|
|
} catch (error) {
|
|
console.warn('Failed to load metadata from cache:', error)
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clear all metadata
|
|
*/
|
|
clearMetadata(): void {
|
|
this.metadata = []
|
|
this.error = null
|
|
this.lastUpdated = null
|
|
this.initialized = false
|
|
|
|
if (process.client) {
|
|
try {
|
|
localStorage.removeItem('metadata-cache')
|
|
} catch (error) {
|
|
console.warn('Failed to clear metadata cache:', error)
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Extract error message from various error types
|
|
*/
|
|
extractErrorMessage(error: unknown): string {
|
|
if (error && typeof error === 'object' && 'statusMessage' in error) {
|
|
return String((error as { statusMessage: string }).statusMessage)
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
return error.message
|
|
}
|
|
|
|
return 'Error inesperado al cargar metadatos'
|
|
},
|
|
|
|
/**
|
|
* Initialize store (called on app startup)
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
// Load from cache first for immediate availability
|
|
this.loadFromCache()
|
|
|
|
// Then try to fetch fresh data
|
|
await this.loadMetadata()
|
|
}
|
|
},
|
|
|
|
// Enable persistence for better UX
|
|
persist: process.client ? {
|
|
key: 'metadata-store',
|
|
storage: localStorage,
|
|
pick: ['metadata', 'lastUpdated', 'initialized']
|
|
} : false
|
|
}) |