diff --git a/.replit b/.replit index 3dc4618..4abcac5 100644 --- a/.replit +++ b/.replit @@ -26,6 +26,10 @@ externalPort = 3003 localPort = 43803 externalPort = 3000 +[[ports]] +localPort = 46817 +externalPort = 3001 + [env] PORT = "5000" diff --git a/client/src/pages/Routers.tsx b/client/src/pages/Routers.tsx index 5b17efd..8149651 100644 --- a/client/src/pages/Routers.tsx +++ b/client/src/pages/Routers.tsx @@ -1,19 +1,109 @@ +import { useState } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; import { queryClient, apiRequest } from "@/lib/queryClient"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Server, Plus, Trash2 } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogFooter, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { Server, Plus, Trash2, Edit, Wifi, WifiOff } from "lucide-react"; import { format } from "date-fns"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { insertRouterSchema, type InsertRouter } from "@shared/schema"; import type { Router } from "@shared/schema"; import { useToast } from "@/hooks/use-toast"; export default function Routers() { const { toast } = useToast(); + const [addDialogOpen, setAddDialogOpen] = useState(false); + const [editDialogOpen, setEditDialogOpen] = useState(false); + const [editingRouter, setEditingRouter] = useState(null); + const [testingRouterId, setTestingRouterId] = useState(null); + const { data: routers, isLoading } = useQuery({ queryKey: ["/api/routers"], }); + const addForm = useForm({ + resolver: zodResolver(insertRouterSchema), + defaultValues: { + name: "", + ipAddress: "", + apiPort: 8728, + username: "", + password: "", + enabled: true, + }, + }); + + const editForm = useForm({ + resolver: zodResolver(insertRouterSchema), + }); + + const addMutation = useMutation({ + mutationFn: async (data: InsertRouter) => { + return await apiRequest("POST", "/api/routers", data); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/routers"] }); + toast({ + title: "Router aggiunto", + description: "Il router è stato configurato con successo", + }); + setAddDialogOpen(false); + addForm.reset(); + }, + onError: (error: any) => { + toast({ + title: "Errore", + description: error.message || "Impossibile aggiungere il router", + variant: "destructive", + }); + }, + }); + + const updateMutation = useMutation({ + mutationFn: async ({ id, data }: { id: string; data: InsertRouter }) => { + return await apiRequest("PUT", `/api/routers/${id}`, data); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["/api/routers"] }); + toast({ + title: "Router aggiornato", + description: "Le modifiche sono state salvate con successo", + }); + setEditDialogOpen(false); + setEditingRouter(null); + editForm.reset(); + }, + onError: (error: any) => { + toast({ + title: "Errore", + description: error.message || "Impossibile aggiornare il router", + variant: "destructive", + }); + }, + }); + const deleteMutation = useMutation({ mutationFn: async (id: string) => { await apiRequest("DELETE", `/api/routers/${id}`); @@ -34,6 +124,56 @@ export default function Routers() { }, }); + const testConnectionMutation = useMutation({ + mutationFn: async (id: string) => { + const response = await apiRequest("POST", `/api/routers/${id}/test`); + return response; + }, + onSuccess: (data: any) => { + toast({ + title: "Connessione riuscita", + description: data.message || "Il router è raggiungibile e le credenziali sono corrette", + }); + setTestingRouterId(null); + }, + onError: (error: any) => { + toast({ + title: "Connessione fallita", + description: error.message || "Impossibile connettersi al router. Verifica IP, porta e credenziali.", + variant: "destructive", + }); + setTestingRouterId(null); + }, + }); + + const handleAddSubmit = (data: InsertRouter) => { + addMutation.mutate(data); + }; + + const handleEditSubmit = (data: InsertRouter) => { + if (editingRouter) { + updateMutation.mutate({ id: editingRouter.id, data }); + } + }; + + const handleEdit = (router: Router) => { + setEditingRouter(router); + editForm.reset({ + name: router.name, + ipAddress: router.ipAddress, + apiPort: router.apiPort, + username: router.username, + password: router.password, + enabled: router.enabled, + }); + setEditDialogOpen(true); + }; + + const handleTestConnection = (id: string) => { + setTestingRouterId(id); + testConnectionMutation.mutate(id); + }; + return (
@@ -43,10 +183,152 @@ export default function Routers() { Gestisci i router connessi al sistema IDS

- + + + + + + + + Aggiungi Router MikroTik + + Configura un nuovo router MikroTik per il sistema IDS. Assicurati che l'API REST sia abilitata. + + + +
+ + ( + + Nome Router + + + + + Nome descrittivo per identificare il router + + + + )} + /> + + ( + + Indirizzo IP + + + + + Indirizzo IP o hostname del router + + + + )} + /> + + ( + + Porta API + + field.onChange(parseInt(e.target.value))} + data-testid="input-port" + /> + + + Porta API MikroTik (default: 8728 per API, 8729 per API-SSL) + + + + )} + /> + + ( + + Username + + + + + + )} + /> + + ( + + Password + + + + + + )} + /> + + ( + +
+ Abilitato + + Attiva il router per il blocco automatico degli IP + +
+ + + +
+ )} + /> + + + + + + + +
+
@@ -114,9 +396,24 @@ export default function Routers() { variant="outline" size="sm" className="flex-1" + onClick={() => handleTestConnection(router.id)} + disabled={testingRouterId === router.id} data-testid={`button-test-${router.id}`} > - Test Connessione + {testingRouterId === router.id ? ( + + ) : ( + + )} + {testingRouterId === router.id ? "Test..." : "Test"} + + + + + + + + ); } diff --git a/server/routes.ts b/server/routes.ts index 09b9884..aac650b 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -27,6 +27,20 @@ export async function registerRoutes(app: Express): Promise { } }); + app.put("/api/routers/:id", async (req, res) => { + try { + const validatedData = insertRouterSchema.parse(req.body); + const router = await storage.updateRouter(req.params.id, validatedData); + if (!router) { + return res.status(404).json({ error: "Router not found" }); + } + res.json(router); + } catch (error) { + console.error('[Router UPDATE] Error:', error); + res.status(400).json({ error: "Invalid router data" }); + } + }); + app.delete("/api/routers/:id", async (req, res) => { try { const success = await storage.deleteRouter(req.params.id); @@ -39,6 +53,67 @@ export async function registerRoutes(app: Express): Promise { } }); + app.post("/api/routers/:id/test", async (req, res) => { + try { + const router = await storage.getRouterById(req.params.id); + if (!router) { + return res.status(404).json({ error: "Router not found" }); + } + + // Test connessione TCP/HTTP al router + const testUrl = `http://${router.ipAddress}:${router.apiPort}`; + + try { + // Timeout di 5 secondi per il test + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(testUrl, { + method: 'GET', + signal: controller.signal, + headers: { + 'Authorization': 'Basic ' + Buffer.from(`${router.username}:${router.password}`).toString('base64') + } + }); + + clearTimeout(timeoutId); + + // Aggiorna lastSync + await storage.updateRouter(router.id, { lastSync: new Date() }); + + res.json({ + success: true, + message: `Router ${router.name} raggiungibile (HTTP ${response.status})`, + status: response.status, + statusText: response.statusText + }); + } catch (fetchError: any) { + console.error('[Router TEST] Connection failed:', fetchError.message); + + // Differenzia gli errori + if (fetchError.name === 'AbortError') { + res.status(408).json({ + error: "Timeout: Il router non risponde entro 5 secondi", + message: `Impossibile connettersi a ${router.ipAddress}:${router.apiPort}. Verifica che il router sia acceso e raggiungibile.` + }); + } else if (fetchError.code === 'ECONNREFUSED') { + res.status(503).json({ + error: "Connessione rifiutata", + message: `Il router a ${router.ipAddress}:${router.apiPort} rifiuta la connessione. Verifica che l'API REST sia abilitata.` + }); + } else { + res.status(503).json({ + error: "Errore di connessione", + message: `${fetchError.message}. Verifica IP, porta e firewall.` + }); + } + } + } catch (error) { + console.error('[Router TEST] Error:', error); + res.status(500).json({ error: "Failed to test router connection" }); + } + }); + // Network Logs app.get("/api/logs", async (req, res) => { try {