Files
amayo/README/AEDITOR_EJEMPLOS_INTEGRACION.md
Shni 781f4398a4 feat: Implement comprehensive security enhancements and deployment guide for AmayoWeb
- Added a detailed deployment guide (DEPLOYMENT_GUIDE.md) for frontend and backend setup.
- Created an index documentation (INDEX.md) summarizing changes and available resources.
- Established Nginx security configuration (NGINX_SECURITY_CONFIG.md) to protect backend IP and enforce rate limiting.
- Developed a backend security guide (SECURITY_BACKEND_GUIDE.md) outlining security measures and best practices.
- Introduced middleware for security, including rate limiting, CORS, and Cloudflare validation.
- Updated frontend components and services to improve security and user experience.
- Implemented logging and monitoring strategies for better security oversight.
2025-11-06 23:44:44 -06:00

15 KiB

🔧 Guía de Integración Práctica

🎯 Integración Rápida (5 minutos)

Paso 1: Inicializar en App.vue

<script setup lang="ts">
import { invoke } from '@tauri-apps/api/core';
import { appDataDir } from '@tauri-apps/api/path';
import { onMounted } from 'vue';

// Importar nuevos componentes
import ActivityLog from './components/ActivityLog.vue';
import ErrorPanel from './components/ErrorPanel.vue';
import BackupManager from './components/BackupManager.vue';

onMounted(async () => {
  // 1. Obtener directorio de datos
  const dataDir = await appDataDir();
  
  // 2. Inicializar managers
  try {
    await invoke('init_managers', { appDataDir: dataDir });
    console.log('✅ Managers inicializados correctamente');
  } catch (error) {
    console.error('❌ Error inicializando managers:', error);
  }
});
</script>

Paso 2: Añadir a la Navegación

<script setup lang="ts">
const currentView = ref<'editor' | 'activity' | 'errors' | 'backups'>('editor');

const menuItems = [
  { id: 'editor', icon: '📝', label: 'Editor' },
  { id: 'activity', icon: '📋', label: 'Actividad', badge: activityCount },
  { id: 'errors', icon: '🐛', label: 'Problemas', badge: errorCount },
  { id: 'backups', icon: '💾', label: 'Respaldos' },
];

// Contadores
const activityCount = ref(0);
const errorCount = ref(0);

// Actualizar contadores
watch(currentView, async (view) => {
  if (view === 'activity') {
    const logs = await invoke('get_activity_logs');
    activityCount.value = logs.length;
  } else if (view === 'errors') {
    const diagnostics = await invoke('get_diagnostics');
    errorCount.value = diagnostics.filter(d => d.severity === 'error').length;
  }
});
</script>

<template>
  <div class="app">
    <Sidebar 
      :items="menuItems"
      :current="currentView"
      @change="currentView = $event"
    />
    
    <div class="main-content">
      <MonacoEditor v-if="currentView === 'editor'" />
      <ActivityLog v-if="currentView === 'activity'" />
      <ErrorPanel v-if="currentView === 'errors'" />
      <BackupManager v-if="currentView === 'backups'" />
    </div>
  </div>
</template>

📋 Caso de Uso 1: Rastrear Ediciones

En MonacoEditor.vue

import { invoke } from '@tauri-apps/api/core';

// Referencias
const editor = ref<monaco.editor.IStandaloneCodeEditor | null>(null);
const currentFile = ref<string>('');
const lastSaveTime = ref(0);

// Detectar cambios
editor.value?.onDidChangeModelContent(async (e) => {
  const content = editor.value!.getValue();
  const now = Date.now();
  
  // Registrar edición (debounce de 5 segundos)
  if (now - lastSaveTime.value > 5000) {
    await invoke('save_activity_log', {
      entry: {
        type: 'edit',
        action: 'Archivo modificado',
        file: currentFile.value,
        lines: content.split('\n').length,
        details: `${e.changes.length} cambios realizados`
      }
    });
    
    lastSaveTime.value = now;
  }
  
  // Analizar errores en tiempo real
  await invoke('analyze_file_diagnostics', {
    filePath: currentFile.value,
    content: content
  });
});

// Guardar archivo
const saveFile = async () => {
  const content = editor.value!.getValue();
  
  try {
    // 1. Guardar archivo
    await invoke('write_file_content', {
      filePath: currentFile.value,
      content: content
    });
    
    // 2. Registrar actividad
    await invoke('save_activity_log', {
      entry: {
        type: 'save',
        action: 'Archivo guardado',
        file: currentFile.value,
        lines: content.split('\n').length,
        details: 'Guardado exitoso'
      }
    });
    
    // 3. Limpiar errores previos
    await invoke('clear_file_diagnostics', {
      filePath: currentFile.value
    });
    
    console.log('✅ Archivo guardado y registrado');
  } catch (error) {
    console.error('❌ Error guardando:', error);
  }
};

// Abrir archivo
const openFile = async (filePath: string) => {
  try {
    const content = await invoke('read_file_content', { filePath });
    
    // Actualizar editor
    editor.value?.setValue(content);
    currentFile.value = filePath;
    
    // Registrar apertura
    await invoke('save_activity_log', {
      entry: {
        type: 'open',
        action: 'Archivo abierto',
        file: filePath,
        details: 'Abierto para edición'
      }
    });
    
    // Analizar errores iniciales
    await invoke('analyze_file_diagnostics', {
      filePath: filePath,
      content: content
    });
  } catch (error) {
    console.error('❌ Error abriendo archivo:', error);
  }
};

🐛 Caso de Uso 2: Panel de Errores Interactivo

En ErrorPanel.vue (uso extendido)

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { invoke } from '@tauri-apps/api/core';

const errorPanelRef = ref<InstanceType<typeof ErrorPanel> | null>(null);

// Navegar al error
const handleErrorNavigation = async (error: DiagnosticError) => {
  // 1. Abrir archivo
  await openFile(error.file);
  
  // 2. Ir a la línea
  editor.value?.revealLineInCenter(error.line);
  editor.value?.setPosition({
    lineNumber: error.line,
    column: error.column
  });
  
  // 3. Seleccionar código problemático
  editor.value?.setSelection({
    startLineNumber: error.line,
    startColumn: error.column,
    endLineNumber: error.line,
    endColumn: error.column + 10
  });
  
  // 4. Foco en editor
  editor.value?.focus();
};

// Quick Fix automático
const applyQuickFix = async (error: DiagnosticError) => {
  const content = editor.value!.getValue();
  const lines = content.split('\n');
  
  if (error.code === 'no-console') {
    // Remover console.log
    lines[error.line - 1] = lines[error.line - 1].replace(/console\.log\(.*?\);?/, '');
  } else if (error.code === 'no-var') {
    // Reemplazar var por const
    lines[error.line - 1] = lines[error.line - 1].replace(/\bvar\b/, 'const');
  } else if (error.code === 'eqeqeq') {
    // Reemplazar == por ===
    lines[error.line - 1] = lines[error.line - 1].replace(/ == /, ' === ');
  }
  
  // Actualizar editor
  editor.value?.setValue(lines.join('\n'));
  
  // Remover error de la lista
  await invoke('clear_file_diagnostics', {
    filePath: error.file
  });
  
  // Re-analizar
  await invoke('analyze_file_diagnostics', {
    filePath: error.file,
    content: lines.join('\n')
  });
};

// Actualizar cada 5 segundos
onMounted(() => {
  setInterval(async () => {
    await errorPanelRef.value?.refreshErrors();
  }, 5000);
});
</script>

<template>
  <ErrorPanel 
    ref="errorPanelRef"
    @navigate-to-error="handleErrorNavigation"
    @apply-fix="applyQuickFix"
  />
</template>

💾 Caso de Uso 3: Sistema de Respaldo Inteligente

Auto-respaldo en eventos críticos

import { invoke } from '@tauri-apps/api/core';

// Crear respaldo antes de operaciones peligrosas
const refactorCommand = async () => {
  // 1. Crear respaldo de seguridad
  try {
    await invoke('create_backup', {
      name: 'Pre-refactor backup',
      description: 'Respaldo automático antes de refactorizar comandos',
      type: 'manual'
    });
    console.log('✅ Respaldo de seguridad creado');
  } catch (error) {
    console.error('❌ Error creando respaldo:', error);
    if (!confirm('No se pudo crear respaldo. ¿Continuar de todos modos?')) {
      return;
    }
  }
  
  // 2. Realizar refactorización
  await performRefactoring();
  
  // 3. Registrar actividad
  await invoke('save_activity_log', {
    entry: {
      type: 'edit',
      action: 'Refactorización completada',
      file: 'múltiples archivos',
      details: 'Refactorización de comandos exitosa'
    }
  });
};

// Respaldo automático cada 5 minutos
let backupTimer: number | null = null;

const startAutoBackup = () => {
  backupTimer = window.setInterval(async () => {
    try {
      await invoke('create_backup', {
        name: `Auto-backup ${new Date().toLocaleTimeString()}`,
        type: 'auto'
      });
      console.log('✅ Auto-backup creado');
    } catch (error) {
      console.error('❌ Error en auto-backup:', error);
    }
  }, 5 * 60 * 1000); // 5 minutos
};

const stopAutoBackup = () => {
  if (backupTimer) {
    clearInterval(backupTimer);
    backupTimer = null;
  }
};

// Restaurar desde respaldo
const restoreFromBackup = async (backupId: string) => {
  if (!confirm('¿Estás seguro? Esto sobrescribirá el código actual.')) {
    return;
  }
  
  try {
    // 1. Crear respaldo del estado actual antes de restaurar
    await invoke('create_backup', {
      name: 'Pre-restore backup',
      description: 'Estado antes de restaurar desde respaldo',
      type: 'manual'
    });
    
    // 2. Restaurar
    await invoke('restore_backup', { backupId });
    
    // 3. Recargar proyecto
    await reloadProject();
    
    alert('✅ Proyecto restaurado exitosamente');
  } catch (error) {
    console.error('❌ Error restaurando:', error);
    alert('Error al restaurar respaldo');
  }
};

🔄 Caso de Uso 4: Comparación de Versiones

En BackupManager.vue

const compareWithBackup = async (backupId: string) => {
  try {
    const { current, backup } = await invoke('compare_backup', { backupId });
    
    // Crear vista de comparación
    showComparisonModal.value = true;
    currentContent.value = current;
    backupContent.value = backup;
    
    // Calcular diferencias
    const diff = calculateDiff(current, backup);
    
    // Mostrar estadísticas
    console.log(`
      📊 Estadísticas de cambios:
      - Líneas añadidas: ${diff.added}
      - Líneas eliminadas: ${diff.removed}
      - Líneas modificadas: ${diff.modified}
    `);
  } catch (error) {
    console.error('❌ Error comparando:', error);
  }
};

// Función para calcular diff simple
const calculateDiff = (current: string, backup: string) => {
  const currentLines = current.split('\n');
  const backupLines = backup.split('\n');
  
  let added = 0;
  let removed = 0;
  let modified = 0;
  
  const maxLength = Math.max(currentLines.length, backupLines.length);
  
  for (let i = 0; i < maxLength; i++) {
    if (!backupLines[i]) {
      added++;
    } else if (!currentLines[i]) {
      removed++;
    } else if (currentLines[i] !== backupLines[i]) {
      modified++;
    }
  }
  
  return { added, removed, modified };
};

🎨 Caso de Uso 5: Notificaciones y Feedback

Sistema de notificaciones

// Crear componente de notificación
const showNotification = (type: 'success' | 'error' | 'info', message: string) => {
  const notification = {
    id: Date.now(),
    type,
    message,
    timestamp: Date.now()
  };
  
  notifications.value.push(notification);
  
  // Auto-remover después de 3 segundos
  setTimeout(() => {
    notifications.value = notifications.value.filter(n => n.id !== notification.id);
  }, 3000);
};

// Usar en operaciones
const saveWithNotification = async () => {
  try {
    await saveFile();
    await invoke('save_activity_log', { entry: {...} });
    showNotification('success', '✅ Archivo guardado correctamente');
  } catch (error) {
    showNotification('error', '❌ Error guardando archivo');
  }
};

const backupWithNotification = async () => {
  try {
    await invoke('create_backup', { type: 'manual' });
    showNotification('success', '✅ Respaldo creado exitosamente');
  } catch (error) {
    showNotification('error', '❌ Error creando respaldo');
  }
};

🔍 Caso de Uso 6: Búsqueda en Activity Log

Filtrado avanzado

// En ActivityLog.vue
const searchTerm = ref('');
const dateRange = ref<[Date, Date] | null>(null);
const selectedTypes = ref<string[]>(['all']);

const filteredLogs = computed(() => {
  let result = logs.value;
  
  // Filtrar por tipo
  if (!selectedTypes.value.includes('all')) {
    result = result.filter(log => selectedTypes.value.includes(log.type));
  }
  
  // Filtrar por búsqueda
  if (searchTerm.value) {
    const term = searchTerm.value.toLowerCase();
    result = result.filter(log => 
      log.action.toLowerCase().includes(term) ||
      log.file.toLowerCase().includes(term) ||
      log.details?.toLowerCase().includes(term)
    );
  }
  
  // Filtrar por rango de fechas
  if (dateRange.value) {
    const [start, end] = dateRange.value;
    result = result.filter(log => {
      const date = new Date(log.timestamp);
      return date >= start && date <= end;
    });
  }
  
  return result;
});

📊 Caso de Uso 7: Dashboard de Estadísticas

Crear vista de resumen

<script setup lang="ts">
const stats = ref({
  totalEdits: 0,
  totalSaves: 0,
  totalErrors: 0,
  totalBackups: 0,
  mostEditedFiles: [] as Array<{ file: string; count: number }>,
  errorDistribution: {} as Record<string, number>
});

const updateStats = async () => {
  // Obtener logs
  const logs = await invoke('get_activity_logs');
  
  // Contar por tipo
  stats.value.totalEdits = logs.filter(l => l.type === 'edit').length;
  stats.value.totalSaves = logs.filter(l => l.type === 'save').length;
  
  // Archivos más editados
  const fileCount: Record<string, number> = {};
  logs.forEach(log => {
    fileCount[log.file] = (fileCount[log.file] || 0) + 1;
  });
  
  stats.value.mostEditedFiles = Object.entries(fileCount)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 5)
    .map(([file, count]) => ({ file, count }));
  
  // Obtener errores
  const errors = await invoke('get_diagnostics');
  stats.value.totalErrors = errors.length;
  
  // Distribución de errores
  errors.forEach(error => {
    const code = error.code || 'unknown';
    stats.value.errorDistribution[code] = 
      (stats.value.errorDistribution[code] || 0) + 1;
  });
  
  // Obtener respaldos
  const backups = await invoke('get_backups');
  stats.value.totalBackups = backups.length;
};

onMounted(() => {
  updateStats();
  // Actualizar cada minuto
  setInterval(updateStats, 60000);
});
</script>

<template>
  <div class="dashboard">
    <h2>📊 Estadísticas de Desarrollo</h2>
    
    <div class="stats-grid">
      <div class="stat-card">
        <span class="stat-icon">✏️</span>
        <span class="stat-value">{{ stats.totalEdits }}</span>
        <span class="stat-label">Ediciones</span>
      </div>
      
      <div class="stat-card">
        <span class="stat-icon">💾</span>
        <span class="stat-value">{{ stats.totalSaves }}</span>
        <span class="stat-label">Guardados</span>
      </div>
      
      <div class="stat-card">
        <span class="stat-icon">🐛</span>
        <span class="stat-value">{{ stats.totalErrors }}</span>
        <span class="stat-label">Errores</span>
      </div>
      
      <div class="stat-card">
        <span class="stat-icon">💾</span>
        <span class="stat-value">{{ stats.totalBackups }}</span>
        <span class="stat-label">Respaldos</span>
      </div>
    </div>
    
    <div class="most-edited">
      <h3>🔥 Archivos Más Editados</h3>
      <ul>
        <li v-for="item in stats.mostEditedFiles" :key="item.file">
          {{ item.file }} <span class="count">({{ item.count }} ediciones)</span>
        </li>
      </ul>
    </div>
  </div>
</template>

Checklist de Implementación

  • Inicializar managers en App.vue
  • Añadir componentes al router/navigation
  • Integrar activity log en MonacoEditor
  • Configurar análisis de errores en tiempo real
  • Activar auto-respaldo cada 5 minutos
  • Añadir notificaciones de feedback
  • Crear atajos de teclado
  • Probar restauración de respaldos
  • Verificar rendimiento con proyecto grande
  • Documentar configuración personalizada

¡Listo para implementar! 🚀