validator.js•5.76 kB
#!/usr/bin/env node
import fs from 'fs/promises';
import path from 'path';
/**
* 🔐 Módulo de Validación TreePod Financial MCP
* Implementa la Guía de Trabajo Fundamental: Nunca inventar datos
*/
export class DataValidator {
constructor() {
this.logs = [];
}
/**
* Valida si un archivo existe y es accesible
*/
async validateFileExists(filePath, description = 'archivo') {
try {
await fs.access(filePath);
this.log('info', `✅ ${description} encontrado: ${filePath}`);
return true;
} catch (error) {
this.log('warning', `⚠️ ${description} no encontrado: ${filePath}`);
return false;
}
}
/**
* Valida si los datos JSON son válidos y contienen campos requeridos
*/
async validateJsonData(filePath, requiredFields = [], description = 'datos') {
try {
const data = await fs.readFile(filePath, 'utf8');
const parsed = JSON.parse(data);
// Verificar campos requeridos
const missingFields = requiredFields.filter(field => !(field in parsed));
if (missingFields.length > 0) {
this.log('error', `❌ ${description} incompletos. Faltan campos: ${missingFields.join(', ')}`);
return { valid: false, data: null, missingFields };
}
this.log('info', `✅ ${description} válidos con ${Object.keys(parsed).length} campos`);
return { valid: true, data: parsed, missingFields: [] };
} catch (error) {
this.log('error', `❌ Error validando ${description}: ${error.message}`);
return { valid: false, data: null, error: error.message };
}
}
/**
* Valida parámetros de entrada del usuario
*/
validateUserInput(params, schema) {
const errors = [];
for (const [key, rules] of Object.entries(schema)) {
const value = params[key];
// Campo requerido
if (rules.required && (value === undefined || value === null || value === '')) {
errors.push(`Campo requerido faltante: ${key}`);
continue;
}
// Tipo de dato
if (value !== undefined && rules.type && typeof value !== rules.type) {
errors.push(`Campo ${key} debe ser tipo ${rules.type}, recibido: ${typeof value}`);
}
// Rango numérico
if (rules.min !== undefined && value < rules.min) {
errors.push(`Campo ${key} debe ser mayor o igual a ${rules.min}`);
}
if (rules.max !== undefined && value > rules.max) {
errors.push(`Campo ${key} debe ser menor o igual a ${rules.max}`);
}
// Valores permitidos
if (rules.enum && !rules.enum.includes(value)) {
errors.push(`Campo ${key} debe ser uno de: ${rules.enum.join(', ')}`);
}
}
if (errors.length > 0) {
this.log('error', `❌ Validación de entrada falló: ${errors.join(', ')}`);
return { valid: false, errors };
}
this.log('info', `✅ Parámetros de entrada válidos`);
return { valid: true, errors: [] };
}
/**
* Valida fechas
*/
validateDateRange(checkinDate, checkoutDate) {
try {
const checkin = new Date(checkinDate);
const checkout = new Date(checkoutDate);
if (isNaN(checkin.getTime()) || isNaN(checkout.getTime())) {
this.log('error', `❌ Fechas inválidas: ${checkinDate}, ${checkoutDate}`);
return { valid: false, error: 'Fechas inválidas' };
}
if (checkout <= checkin) {
this.log('error', `❌ Fecha de salida debe ser posterior a entrada`);
return { valid: false, error: 'Fecha de salida debe ser posterior a entrada' };
}
const nights = Math.ceil((checkout - checkin) / (1000 * 60 * 60 * 24));
this.log('info', `✅ Rango de fechas válido: ${nights} noches`);
return { valid: true, nights, checkin, checkout };
} catch (error) {
this.log('error', `❌ Error validando fechas: ${error.message}`);
return { valid: false, error: error.message };
}
}
/**
* Sistema de logging con trazabilidad
*/
log(level, message, context = {}) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
level,
message,
context,
source: 'TreePod-Financial-MCP'
};
this.logs.push(logEntry);
// También log a consola para debugging
const prefix = level === 'error' ? '❌' : level === 'warning' ? '⚠️' : '✅';
console.error(`[${timestamp}] ${prefix} ${message}`);
// Si hay demasiados logs, mantener solo los últimos 100
if (this.logs.length > 100) {
this.logs = this.logs.slice(-100);
}
}
/**
* Obtiene logs recientes para trazabilidad
*/
getRecentLogs(count = 10) {
return this.logs.slice(-count);
}
/**
* Genera respuesta de error estándar cuando no hay datos suficientes
*/
generateInsufficientDataResponse(dataType, suggestion = null) {
const message = `❌ **DATOS INSUFICIENTES**\n\n` +
`No tengo suficiente información sobre **${dataType}** para generar una respuesta confiable.\n\n` +
`**¿Qué puedes hacer?**\n` +
`• Verifica que los archivos de datos estén disponibles\n` +
`• Contacta al administrador del sistema\n` +
`• Intenta nuevamente en unos minutos\n\n` +
(suggestion ? `**Sugerencia:** ${suggestion}\n\n` : '') +
`*TreePod Financial MCP - Siguiendo principios de transparencia de datos*`;
this.log('info', `Respuesta de datos insuficientes generada para: ${dataType}`);
return {
content: [{
type: 'text',
text: message
}]
};
}
}
// Instancia global del validador
export const validator = new DataValidator();