Merge: Integrar Sistema de Análisis de Seguridad v1.6.0 a main

CARACTERÍSTICAS PRINCIPALES v1.6.0:
- Sistema de auditoría multi-tenant completo
- Análisis de seguridad con detección de amenazas en tiempo real
- Panel de seguridad con 4 algoritmos de detección:
  * Ataques de fuerza bruta
  * Escalada de privilegios
  * Eliminaciones masivas
  * Cuentas comprometidas
- Acciones de seguridad: bloquear IP, resetear contraseña, notificar admin
- Cross-tenant viewing para ADMIN/SUPPORT_MANAGER
- Frontend completamente funcional con nuevo menú Seguridad
- Sistema completamente verificado y operativo

Resolución de conflictos:
- Versiones actualizadas a 1.6.0 en todos los package.json y pyproject.toml
- Menú de seguridad integrado en Sidebar
- Tickets endpoint actualizado con auditoría
- Correcciones de middleware y queries SQL aplicadas
This commit is contained in:
2026-02-16 11:04:30 -07:00
26 changed files with 3970 additions and 105 deletions

View File

@@ -1,8 +1,9 @@
<script>
import { createEventDispatcher, onMount, onDestroy } from 'svelte';
import { createEventDispatcher } from 'svelte';
export let open = false;
export let title = '';
export let size = 'lg'; // sm, md, lg, xl, 2xl
const dispatch = createEventDispatcher();
@@ -15,6 +16,20 @@
close();
}
}
function handleBackdropClick(e) {
if (e.target === e.currentTarget) {
close();
}
}
const sizeClasses = {
sm: 'sm:max-w-sm',
md: 'sm:max-w-md',
lg: 'sm:max-w-lg',
xl: 'sm:max-w-xl',
'2xl': 'sm:max-w-2xl'
};
</script>
<svelte:window on:keydown={handleKeydown}/>
@@ -23,21 +38,45 @@
<div class="fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75" aria-hidden="true" on:click={close}></div>
<!-- Backdrop -->
<div
class="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75"
aria-hidden="true"
on:click={handleBackdropClick}
></div>
<!-- Center trick -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<div class="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
<div class="sm:flex sm:items-start">
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
{title}
</h3>
<div class="mt-2 text-sm text-gray-500">
<slot />
</div>
</div>
<!-- Modal panel -->
<div class="inline-block w-full align-bottom bg-white rounded-lg shadow-xl transform transition-all sm:my-8 sm:align-middle {sizeClasses[size]} sm:w-full">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h3 class="text-lg font-semibold text-gray-900" id="modal-title">
{title}
</h3>
<button
type="button"
on:click={close}
class="text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 rounded-lg p-1"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Body -->
<div class="px-6 py-4 max-h-[70vh] overflow-y-auto">
<slot />
</div>
<!-- Footer (optional) -->
{#if $$slots.footer}
<div class="px-6 py-4 bg-gray-50 border-t border-gray-200 rounded-b-lg">
<slot name="footer" />
</div>
{/if}
</div>
</div>
</div>

View File

@@ -62,7 +62,21 @@
name: 'Auditoría',
href: '/audit',
icon: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z'
}
},
{
name: 'Seguridad',
href: '/audit/security',
icon: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z'
}
);
}
return baseNavigation;
}
function isCurrentPage(href: string) {
return $page.url.pathname === href || ($page.url.pathname.startsWith(href) && href !== '/');
}
</script>
<!-- Mobile sidebar backdrop -->
@@ -169,5 +183,10 @@
</div>
</div>
</div>
<!-- Version info -->
<div class="px-4 py-2 border-t border-gray-100">
<p class="text-xs text-gray-400 text-center">v1.6.0</p>
</div>
</div>
</div>