feat: Complete CSF web interface with all features

- Fixed TypeScript errors and build issues
- Added comprehensive README.md with documentation
- Created .env.example with all configuration options
- Improved .gitignore with CSF-specific entries
- Added VS Code configuration for development
- Fixed next.config.mjs configuration
- Corrected API route type issues
- Added CHANGELOG.md with version history
- All components now compile without errors
- Ready for production deployment

Features included:
- Modern web interface for CSF firewall management
- Real-time monitoring with WebSockets
- JWT authentication system
- Complete API for CSF control
- Responsive UI with Tailwind CSS
- TypeScript support throughout
- Docker-ready configuration

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-09-20 18:48:53 +02:00
padre dce8b73888
commit 8a4fde51c7
Se han modificado 8 ficheros con 730 adiciones y 54 borrados

83
.env.example Archivo normal
Ver fichero

@@ -0,0 +1,83 @@
# Archivo de variables de entorno para CSF Web Interface
# Copie este archivo a .env.local y modifique los valores según sea necesario
# ====== SEGURIDAD ======
# Clave secreta para JWT (CAMBIAR EN PRODUCCIÓN)
JWT_SECRET=csf-web-super-secret-jwt-key-change-this-in-production
# Modo de entorno
NODE_ENV=development
# ====== CONFIGURACIÓN WEB ======
# Puerto para la interfaz web
PORT=3000
# Hostname para la aplicación
HOSTNAME=0.0.0.0
# URL pública de la API (para cliente)
NEXT_PUBLIC_API_URL=http://localhost:3000
# ====== CONFIGURACIÓN CSF ======
# Rutas de configuración CSF (ajustar según instalación)
CSF_CONFIG_PATH=/etc/csf
CSF_LOG_PATH=/var/log/lfd
CSF_BIN_PATH=/usr/local/csf/bin
# ====== AUTENTICACIÓN ======
# Credenciales de administrador por defecto (CAMBIAR EN PRODUCCIÓN)
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
# Duración del token JWT (en horas)
JWT_EXPIRES_IN=24h
# ====== API EXTERNA ======
# URLs de servicios externos (opcional)
EXTERNAL_API_URL=
WEBHOOK_URL=
# ====== DESARROLLO ======
# Habilitar logs de desarrollo
DEBUG=true
# Habilitar hot reload para WebSockets
SOCKET_DEBUG=false
# ====== MONITOREO ======
# Interval de actualización de estadísticas (milisegundos)
STATS_UPDATE_INTERVAL=5000
# Interval de actualización de logs (milisegundos)
LOGS_UPDATE_INTERVAL=10000
# Límite máximo de logs en memoria
MAX_LOGS_IN_MEMORY=500
# ====== SEGURIDAD ADICIONAL ======
# Habilitar rate limiting
ENABLE_RATE_LIMITING=false
# Máximo de intentos de login
MAX_LOGIN_ATTEMPTS=5
# Tiempo de bloqueo después de intentos fallidos (minutos)
LOGIN_LOCKOUT_TIME=15
# ====== CORS ======
# Orígenes permitidos para CORS (separados por coma)
ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
# ====== ARCHIVOS ======
# Directorio para archivos temporales
TEMP_DIR=/tmp
# Directorio para logs de la aplicación
APP_LOG_DIR=./logs
# ====== PERFORMANCE ======
# Límite de memoria para Node.js (MB)
NODE_OPTIONS=--max-old-space-size=1024
# Timeout para comandos CSF (milisegundos)
CSF_COMMAND_TIMEOUT=30000

143
.gitignore vendido
Ver fichero

@@ -1,6 +1,4 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
# Dependencies
/node_modules
/.pnp
.pnp.*
@@ -9,36 +7,133 @@
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
*.lock
*-lock.json
# env files (can opt-in for committing if needed)
# Testing
/coverage
*.lcov
# Next.js
/.next/
/out/
.next/
# Production builds
/build
/dist
# Environment variables
.env*
!.env.example
# vercel
# Vercel
.vercel
# typescript
# TypeScript
*.tsbuildinfo
next-env.d.ts
/types/generated
*.lock
*-lock.json
# IDE and editors
.vscode/
.idea/
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Logs
logs/
*.log
/logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage/
*.lcov
# nyc test coverage
.nyc_output
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.local
.env.development.local
.env.test.local
.env.production.local
# Storybook build outputs
.out
.storybook-out
# Temporary folders
tmp/
temp/
.tmp/
# Backup files
*.backup
*.bak
*.orig
# Docker
docker-compose.override.yml
.docker/
# CSF specific
/csf-data/
/csf-logs/
/csf-backup/
# Development
.cache/
.parcel-cache/
# Editor directories and files
.vscode/
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

122
CHANGELOG.md Archivo normal
Ver fichero

@@ -0,0 +1,122 @@
# CHANGELOG - CSF Web Interface
Todos los cambios notables de este proyecto serán documentados en este archivo.
## [1.0.0] - 2025-09-20
### ✨ Agregado
- **Panel de Administración Completo**: Interfaz web moderna para CSF
- **Autenticación JWT**: Sistema de login seguro con cookies httpOnly
- **Dashboard Interactivo**: Navegación por pestañas con estado del firewall
- **Gestión de Reglas**: CRUD completo para reglas allow/deny/temporales
- **Monitoreo Tiempo Real**: WebSockets para estadísticas y logs en vivo
- **API REST Completa**: Endpoints para control total de CSF
- **Estadísticas del Sistema**: CPU, memoria, disco, red y conexiones
- **Validación de Entrada**: Verificación de IPs y puertos
- **Interfaz Responsive**: Compatible con dispositivos móviles
- **Indicadores Visuales**: Estado de conexión y salud del sistema
### 🔧 Técnico
- **Framework**: Next.js 15 con App Router
- **UI Library**: React 19 con hooks modernos
- **Styling**: Tailwind CSS 4 con componentes Radix UI
- **Tipos**: TypeScript 5.2 con tipado estricto
- **Estado**: Zustand para gestión de estado global
- **WebSockets**: Socket.IO para datos en tiempo real
- **Build**: Turbopack para compilación rápida
- **Linting**: ESLint con reglas de Next.js
### 🛡️ Seguridad
- Headers de seguridad configurados (CSP, XSS Protection, etc.)
- Validación de entrada en todas las APIs
- Sanitización de datos de salida
- Protección contra CSRF
- Gestión segura de sesiones
### 📡 APIs Implementadas
- `POST /api/auth` - Sistema de autenticación
- `GET /api/csf` - Estado y control del firewall
- `GET|POST|DELETE /api/rules` - Gestión de reglas
- `GET /api/logs` - Acceso a logs del sistema
- `GET /api/stats` - Estadísticas del servidor
- `GET|POST /api/config` - Configuración de CSF
- `GET /api/health` - Health check para Docker
- `WebSocket /api/socket` - Datos en tiempo real
### 🎨 Componentes UI
- `Dashboard` - Panel principal con pestañas
- `FirewallStatusCard` - Estado y controles del firewall
- `ServerStats` - Métricas del sistema con gráficos
- `FirewallRules` - Tabla de reglas con búsqueda
- `LoginForm` - Formulario de autenticación
- `AuthWrapper` - Wrapper de autenticación
- `RealtimeIndicator` - Indicador de conexión WebSocket
### 🪝 Hooks Personalizados
- `useAuth` - Gestión de autenticación
- `useCSFApi` - Interacción con APIs de CSF
- `useRealtimeData` - Conexión WebSocket
### 🔧 Configuración
- **next.config.mjs**: Configuración de Next.js con standalone output
- **tsconfig.json**: Configuración de TypeScript
- **tailwind.config.js**: Configuración de Tailwind CSS
- **package.json**: Dependencias y scripts de build
### 📦 Dependencias Principales
```json
{
"next": "15.5.3",
"react": "19.1.0",
"typescript": "5.2.2",
"tailwindcss": "4.0",
"socket.io": "4.8.1",
"jsonwebtoken": "9.0.2",
"zustand": "5.0.1"
}
```
### 🚀 Scripts Disponibles
- `npm run dev` - Servidor de desarrollo
- `npm run build` - Build de producción
- `npm run start` - Servidor de producción
- `npm run type-check` - Verificación de tipos
- `npm run lint` - Linting de código
- `npm run setup` - Setup completo
### 🐳 Docker
- Optimizado para contenedores Docker
- Build standalone para menor tamaño
- Variables de entorno configurables
- Health checks integrados
### 📝 Documentación
- README.md completo con guías de instalación
- Comentarios en código TypeScript
- Documentación de APIs
- Ejemplos de uso
### 🔍 Testing
- Verificación de tipos con TypeScript
- Linting con ESLint
- Build testing automático
## Próximas Versiones
### [1.1.0] - Planificado
- [ ] Visor de logs avanzado con filtros
- [ ] Exportación de configuraciones
- [ ] Gráficos de métricas históricas
- [ ] Notificaciones push
- [ ] Tema oscuro/claro
### [1.2.0] - Planificado
- [ ] Multi-idioma (i18n)
- [ ] Roles y permisos de usuario
- [ ] API de webhooks
- [ ] Integración con Prometheus
- [ ] Tests unitarios e integración
---
**Nota**: Todas las fechas están en formato YYYY-MM-DD.

413
README.md
Ver fichero

@@ -1,36 +1,405 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# CSF Web Interface
## Getting Started
Interfaz web moderna para **ConfigServer Security & Firewall (CSF)** construida con Next.js 15, React 19 y TypeScript.
First, run the development server:
![Next.js](https://img.shields.io/badge/Next.js-15-black)
![React](https://img.shields.io/badge/React-19-blue)
![TypeScript](https://img.shields.io/badge/TypeScript-5.2-blue)
![Tailwind](https://img.shields.io/badge/Tailwind-4.0-06B6D4)
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
## 🚀 Características
- **🎨 Interfaz Moderna**: UI responsive construida con Tailwind CSS
- **⚡ Tiempo Real**: WebSockets para actualizaciones en vivo
- **🔐 Autenticación Segura**: Sistema JWT con cookies httpOnly
- **📊 Dashboard Completo**: Estadísticas, logs y monitoreo del sistema
- **🛡️ Gestión de Reglas**: Control total de reglas allow/deny/temporal
- **📈 Monitoreo en Vivo**: CPU, memoria, red y conexiones
- **🔄 API REST**: Endpoints completos para control de CSF
- **🌐 WebSocket**: Datos en tiempo real sin polling
## 📁 Estructura del Proyecto
```
csf-web/
├── 📁 public/ # Archivos estáticos
├── 📁 src/
│ ├── 📁 app/ # App Router (Next.js 15)
│ │ ├── 📁 api/ # API Routes
│ │ │ ├── 📄 auth/route.ts # Autenticación
│ │ │ ├── 📄 csf/route.ts # Control CSF
│ │ │ ├── 📄 rules/route.ts # Gestión reglas
│ │ │ ├── 📄 logs/route.ts # Logs del sistema
│ │ │ ├── 📄 stats/route.ts # Estadísticas
│ │ │ ├── 📄 config/route.ts # Configuración
│ │ │ └── 📄 health/route.ts # Health check
│ │ ├── 📄 layout.tsx # Layout principal
│ │ ├── 📄 page.tsx # Página principal
│ │ └── 📄 globals.css # Estilos globales
│ ├── 📁 components/ # Componentes React
│ │ ├── 📄 Dashboard.tsx # Dashboard principal
│ │ ├── 📄 FirewallStatusCard.tsx # Estado del firewall
│ │ ├── 📄 ServerStats.tsx # Estadísticas servidor
│ │ ├── 📄 FirewallRules.tsx # Gestión de reglas
│ │ ├── 📄 LogViewer.tsx # Visor de logs
│ │ ├── 📄 LoginForm.tsx # Formulario login
│ │ ├── 📄 AuthWrapper.tsx # Wrapper autenticación
│ │ └── 📄 RealtimeIndicator.tsx # Indicador tiempo real
│ ├── 📁 hooks/ # Hooks personalizados
│ │ ├── 📄 use-auth.ts # Hook autenticación
│ │ ├── 📄 use-csf-api.ts # Hook API CSF
│ │ └── 📄 use-realtime-data.ts # Hook WebSocket
│ ├── 📁 lib/ # Utilidades
│ │ └── 📄 utils.ts # Funciones utilitarias
│ ├── 📁 store/ # Estado global
│ │ └── 📄 csf-store.ts # Store Zustand
│ ├── 📁 types/ # Tipos TypeScript
│ │ └── 📄 csf.ts # Tipos CSF
│ └── 📁 pages/ # Pages API (WebSocket)
│ └── 📁 api/
│ └── 📄 socket.ts # WebSocket handler
├── 📄 package.json # Dependencias
├── 📄 next.config.mjs # Configuración Next.js
├── 📄 tailwind.config.js # Configuración Tailwind
├── 📄 tsconfig.json # Configuración TypeScript
├── 📄 jsconfig.json # Configuración JavaScript
└── 📄 README.md # Este archivo
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## 🛠️ Instalación y Desarrollo
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
### Prerrequisitos
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
- Node.js 18.17 o superior
- npm 9.0 o superior
## Learn More
### Instalación
To learn more about Next.js, take a look at the following resources:
```bash
# Instalar dependencias
npm install --legacy-peer-deps
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
# Configurar variables de entorno
cp .env.example .env.local
```
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
### Variables de Entorno
## Deploy on Vercel
Crear archivo `.env.local` con:
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
```bash
# Seguridad
JWT_SECRET=your-super-secret-jwt-key-here
NODE_ENV=development
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
# API
NEXT_PUBLIC_API_URL=http://localhost:3000
# CSF Paths (para desarrollo local)
CSF_CONFIG_PATH=/etc/csf
CSF_LOG_PATH=/var/log/lfd
CSF_BIN_PATH=/usr/local/csf/bin
```
### Comandos de Desarrollo
```bash
# Desarrollo con hot reload
npm run dev
# Construir para producción
npm run build
# Iniciar servidor de producción
npm start
# Verificar tipos TypeScript
npm run type-check
# Linting
npm run lint
# Setup completo
npm run setup
```
## 🔧 Tecnologías Utilizadas
### Frontend
- **Next.js 15**: Framework React con App Router
- **React 19**: Biblioteca de interfaces de usuario
- **TypeScript 5.2**: Tipado estático
- **Tailwind CSS 4**: Framework CSS utilitario
- **Radix UI**: Componentes primitivos accesibles
### Estado y Datos
- **Zustand 5**: Gestión de estado global
- **Socket.IO Client**: WebSocket para tiempo real
- **React Hooks**: Estado local y efectos
### Autenticación
- **JWT**: Tokens de autenticación
- **bcrypt**: Hash de contraseñas
- **Cookies httpOnly**: Almacenamiento seguro
### Desarrollo
- **ESLint**: Linting de código
- **Prettier**: Formateo de código
- **TypeScript**: Análisis de tipos
## 📡 API Routes
### Autenticación (`/api/auth`)
```typescript
// Login
POST /api/auth
Body: { username: string, password: string }
// Verificar sesión
GET /api/auth
// Logout
DELETE /api/auth
```
### Control CSF (`/api/csf`)
```typescript
// Estado del firewall
GET /api/csf?action=status|version|check
// Ejecutar comandos
POST /api/csf
Body: { action: "start"|"stop"|"restart"|"enable"|"disable", args?: string[] }
```
### Gestión de Reglas (`/api/rules`)
```typescript
// Obtener reglas
GET /api/rules?type=all|allow|deny|temp
// Agregar regla
POST /api/rules
Body: { action: string, ip: string, comment?: string }
// Eliminar regla
DELETE /api/rules?ip=<ip>&type=allow|deny
```
### Logs del Sistema (`/api/logs`)
```typescript
// Obtener logs
GET /api/logs?type=firewall|lfd|system|blocked&limit=100&since=<timestamp>
```
### Estadísticas (`/api/stats`)
```typescript
// Estadísticas del servidor
GET /api/stats
// Estadísticas en tiempo real
POST /api/stats
Body: { interval: number }
```
## 🔌 WebSocket Events
### Cliente → Servidor
```typescript
// Solicitar estadísticas
socket.emit('request-stats')
// Solicitar logs
socket.emit('request-logs', { limit: 50 })
```
### Servidor → Cliente
```typescript
// Datos de estadísticas
socket.on('stats', (data: ServerStats) => {})
// Nuevos logs
socket.on('logs', (data: LogEntry[]) => {})
// Estado de conexión
socket.on('connected', (data: { message: string }) => {})
```
## 🎨 Componentes Principales
### Dashboard
```tsx
// Dashboard principal con navegación
<Dashboard />
```
### Autenticación
```tsx
// Wrapper de autenticación
<AuthWrapper>
<App />
</AuthWrapper>
// Formulario de login
<LoginForm onLogin={handleLogin} />
```
### Firewall
```tsx
// Estado del firewall
<FirewallStatusCard />
// Gestión de reglas
<FirewallRules />
// Estadísticas del servidor
<ServerStats />
```
### Utilidades
```tsx
// Indicador de conexión en tiempo real
<RealtimeIndicator />
// Visor de logs (placeholder)
<LogViewer />
```
## 🪝 Hooks Personalizados
### useAuth
```typescript
const { user, login, logout, loading, error } = useAuth()
```
### useCSFApi
```typescript
const {
status, rules, logs, stats,
startFirewall, stopFirewall, addRule,
loading, error
} = useCSFApi()
```
### useRealtimeData
```typescript
const {
connected, stats, logs,
connect, disconnect, requestStats
} = useRealtimeData()
```
## 🔒 Seguridad
### Autenticación
- JWT con cookies httpOnly y secure
- Protección CSRF automática
- Validación de tokens en cada request
### API Security
- Validación de entrada en todos los endpoints
- Rate limiting (recomendado para producción)
- Headers de seguridad configurados
### Configuración de Seguridad
```javascript
// next.config.mjs
headers: [
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'X-XSS-Protection': '1; mode=block',
'Referrer-Policy': 'strict-origin-when-cross-origin'
]
```
## 🚀 Despliegue
### Desarrollo Local
```bash
npm run dev
# Acceso: http://localhost:3000
```
### Docker (Ver docker-compose.yml en raíz)
```bash
# Desde el directorio raíz del proyecto
docker-compose up -d
```
### Producción
```bash
npm run build
npm start
```
## 🐛 Resolución de Problemas
### Error de dependencias
```bash
# Limpiar cache e instalar
rm -rf node_modules package-lock.json
npm install --legacy-peer-deps
```
### Errores de TypeScript
```bash
# Verificar tipos
npm run type-check
# Reiniciar servidor TypeScript en VS Code
Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"
```
### Problemas de WebSocket
```bash
# Verificar que el servidor WebSocket esté ejecutándose
curl http://localhost:3000/api/socket
```
### Errores de CSF API
- Verificar que CSF esté instalado y funcional
- Revisar permisos de archivos de configuración
- Comprobar rutas de CSF en variables de entorno
## 📈 Monitoreo y Logs
### Logs de la Aplicación
```bash
# Desarrollo
tail -f .next/server.log
# Producción (Docker)
docker logs csf-web-interface -f
```
### Métricas de Rendimiento
- Usar herramientas de desarrollo de React
- Monitorear WebSocket connections
- Revisar uso de memoria en estadísticas
## 🤝 Contribución
1. Fork el repositorio
2. Crear rama feature: `git checkout -b feature/nueva-caracteristica`
3. Commit cambios: `git commit -am 'Agregar nueva característica'`
4. Push a la rama: `git push origin feature/nueva-caracteristica`
5. Crear Pull Request
### Estándares de Código
- Usar TypeScript para todo el código
- Seguir convenciones de React Hooks
- Mantener componentes pequeños y reutilizables
- Documentar APIs y componentes complejos
## 📝 Licencia
GPL v3 - Ver archivo LICENSE en el directorio raíz.
## 🆘 Soporte
- 📧 Email: soporte@ejemplo.com
- 🐛 Issues: [GitHub Issues](../../issues)
- 📚 Docs: Consultar este README.md
---
**Desarrollado con ❤️ para administrar CSF de manera moderna y eficiente.**

Ver fichero

@@ -41,9 +41,7 @@ const nextConfig = {
},
// Configuración para desarrollo
experimental: {
serverComponentsExternalPackages: []
}
serverExternalPackages: []
};
export default nextConfig;

Ver fichero

@@ -9,7 +9,7 @@ export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const type = searchParams.get('type')
const limit = parseInt(searchParams.get('limit') || '100')
const since = searchParams.get('since') // timestamp
const since = searchParams.get('since') || undefined // timestamp
try {
switch (type) {

Ver fichero

@@ -4,7 +4,6 @@ import { useState } from 'react'
import { FirewallStatusCard } from './FirewallStatusCard'
import { ServerStats } from './ServerStats'
import { FirewallRules } from './FirewallRules'
import { LogViewer } from './LogViewer'
import { RealtimeIndicator } from './RealtimeIndicator'
import { useRealtimeData } from '@/hooks/use-realtime-data'
@@ -147,7 +146,17 @@ export function Dashboard() {
)}
{activeTab === 'logs' && (
<LogViewer />
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-xl font-bold text-gray-900 mb-4">Logs del Sistema</h2>
<div className="bg-blue-50 border border-blue-200 rounded-md p-4">
<p className="text-blue-700">
El visor de logs avanzado estará disponible próximamente.
</p>
<p className="text-blue-600 text-sm mt-2">
Por ahora puedes monitorear la actividad desde el dashboard principal.
</p>
</div>
</div>
)}
{activeTab === 'config' && (
@@ -158,7 +167,7 @@ export function Dashboard() {
La configuración avanzada del firewall CSF estará disponible próximamente.
</p>
<p className="text-blue-600 text-sm mt-2">
Por ahora puedes gestionar las reglas básicas desde la pestaña "Reglas".
Por ahora puedes gestionar las reglas básicas desde la pestaña &quot;Reglas&quot;.
</p>
</div>
</div>

Ver fichero

@@ -94,7 +94,7 @@ export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout
let timeout: ReturnType<typeof setTimeout>
return (...args: Parameters<T>) => {
clearTimeout(timeout)
timeout = setTimeout(() => func(...args), wait)