ids.alfacom.it/client/src/pages/Routers.tsx
marco370 7eb0991cb5 Update Mikrotik router connection settings and remove redundant tests
Removes the connection testing functionality and updates the default API port to 8729 for Mikrotik routers.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 54ecaeb2-ec77-4629-8d8d-e3bc4f663bec
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/31VdIyL
2025-11-25 11:29:12 +00:00

536 lines
19 KiB
TypeScript

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 {
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 } 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<Router | null>(null);
const { data: routers, isLoading } = useQuery<Router[]>({
queryKey: ["/api/routers"],
});
const addForm = useForm<InsertRouter>({
resolver: zodResolver(insertRouterSchema),
defaultValues: {
name: "",
ipAddress: "",
apiPort: 8729,
username: "",
password: "",
enabled: true,
},
});
const editForm = useForm<InsertRouter>({
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}`);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["/api/routers"] });
toast({
title: "Router eliminato",
description: "Il router è stato rimosso con successo",
});
},
onError: () => {
toast({
title: "Errore",
description: "Impossibile eliminare il router",
variant: "destructive",
});
},
});
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);
};
return (
<div className="flex flex-col gap-6 p-6" data-testid="page-routers">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-semibold" data-testid="text-page-title">Router MikroTik</h1>
<p className="text-muted-foreground" data-testid="text-page-subtitle">
Gestisci i router connessi al sistema IDS
</p>
</div>
<Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>
<DialogTrigger asChild>
<Button data-testid="button-add-router">
<Plus className="h-4 w-4 mr-2" />
Aggiungi Router
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[500px]" data-testid="dialog-add-router">
<DialogHeader>
<DialogTitle>Aggiungi Router MikroTik</DialogTitle>
<DialogDescription>
Configura un nuovo router MikroTik per il sistema IDS. Assicurati che l'API RouterOS (porta 8729/8728) sia abilitata.
</DialogDescription>
</DialogHeader>
<Form {...addForm}>
<form onSubmit={addForm.handleSubmit(handleAddSubmit)} className="space-y-4">
<FormField
control={addForm.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nome Router</FormLabel>
<FormControl>
<Input placeholder="es. MikroTik Ufficio" {...field} data-testid="input-name" />
</FormControl>
<FormDescription>
Nome descrittivo per identificare il router
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={addForm.control}
name="ipAddress"
render={({ field }) => (
<FormItem>
<FormLabel>Indirizzo IP</FormLabel>
<FormControl>
<Input placeholder="es. 192.168.1.1" {...field} data-testid="input-ip" />
</FormControl>
<FormDescription>
Indirizzo IP o hostname del router
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={addForm.control}
name="apiPort"
render={({ field }) => (
<FormItem>
<FormLabel>Porta API</FormLabel>
<FormControl>
<Input
type="number"
placeholder="8729"
{...field}
onChange={(e) => field.onChange(parseInt(e.target.value))}
data-testid="input-port"
/>
</FormControl>
<FormDescription>
Porta RouterOS API MikroTik (8729 per API-SSL, 8728 per API)
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={addForm.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="admin" {...field} data-testid="input-username" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={addForm.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} data-testid="input-password" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={addForm.control}
name="enabled"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>Abilitato</FormLabel>
<FormDescription>
Attiva il router per il blocco automatico degli IP
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
data-testid="switch-enabled"
/>
</FormControl>
</FormItem>
)}
/>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => setAddDialogOpen(false)}
data-testid="button-cancel"
>
Annulla
</Button>
<Button
type="submit"
disabled={addMutation.isPending}
data-testid="button-submit"
>
{addMutation.isPending ? "Salvataggio..." : "Salva Router"}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
</div>
<Card data-testid="card-routers">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Server className="h-5 w-5" />
Router Configurati ({routers?.length || 0})
</CardTitle>
</CardHeader>
<CardContent>
{isLoading ? (
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
Caricamento...
</div>
) : routers && routers.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{routers.map((router) => (
<div
key={router.id}
className="p-4 rounded-lg border hover-elevate"
data-testid={`router-card-${router.id}`}
>
<div className="flex items-start justify-between mb-3">
<div>
<h3 className="font-semibold text-lg" data-testid={`text-name-${router.id}`}>
{router.name}
</h3>
<p className="text-sm font-mono text-muted-foreground" data-testid={`text-ip-${router.id}`}>
{router.ipAddress}:{router.apiPort}
</p>
</div>
<Badge
variant={router.enabled ? "default" : "secondary"}
data-testid={`badge-status-${router.id}`}
>
{router.enabled ? "Attivo" : "Disabilitato"}
</Badge>
</div>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Username:</span>
<span className="font-mono" data-testid={`text-username-${router.id}`}>
{router.username}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Creato:</span>
<span data-testid={`text-created-${router.id}`}>
{format(new Date(router.createdAt), "dd/MM/yyyy")}
</span>
</div>
{router.lastSync && (
<div className="flex justify-between">
<span className="text-muted-foreground">Ultima sync:</span>
<span data-testid={`text-sync-${router.id}`}>
{format(new Date(router.lastSync), "HH:mm:ss")}
</span>
</div>
)}
</div>
<div className="flex gap-2 mt-4">
<Button
variant="outline"
size="sm"
className="flex-1"
onClick={() => handleEdit(router)}
data-testid={`button-edit-${router.id}`}
>
<Edit className="h-4 w-4 mr-2" />
Modifica
</Button>
<Button
variant="outline"
size="sm"
onClick={() => deleteMutation.mutate(router.id)}
disabled={deleteMutation.isPending}
data-testid={`button-delete-${router.id}`}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-12 text-muted-foreground" data-testid="text-no-routers">
<Server className="h-12 w-12 mx-auto mb-2 opacity-50" />
<p className="mb-2">Nessun router configurato</p>
<p className="text-sm">Aggiungi il primo router per iniziare</p>
</div>
)}
</CardContent>
</Card>
<Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
<DialogContent className="sm:max-w-[500px]" data-testid="dialog-edit-router">
<DialogHeader>
<DialogTitle>Modifica Router</DialogTitle>
<DialogDescription>
Modifica le impostazioni del router {editingRouter?.name}
</DialogDescription>
</DialogHeader>
<Form {...editForm}>
<form onSubmit={editForm.handleSubmit(handleEditSubmit)} className="space-y-4">
<FormField
control={editForm.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>Nome Router</FormLabel>
<FormControl>
<Input placeholder="es. MikroTik Ufficio" {...field} data-testid="input-edit-name" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={editForm.control}
name="ipAddress"
render={({ field }) => (
<FormItem>
<FormLabel>Indirizzo IP</FormLabel>
<FormControl>
<Input placeholder="es. 192.168.1.1" {...field} data-testid="input-edit-ip" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={editForm.control}
name="apiPort"
render={({ field }) => (
<FormItem>
<FormLabel>Porta API</FormLabel>
<FormControl>
<Input
type="number"
placeholder="8729"
{...field}
onChange={(e) => field.onChange(parseInt(e.target.value))}
data-testid="input-edit-port"
/>
</FormControl>
<FormDescription>
Porta RouterOS API MikroTik (8729 per API-SSL, 8728 per API)
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={editForm.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="admin" {...field} data-testid="input-edit-username" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={editForm.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="••••••••" {...field} data-testid="input-edit-password" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={editForm.control}
name="enabled"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
<div className="space-y-0.5">
<FormLabel>Abilitato</FormLabel>
<FormDescription>
Attiva il router per il blocco automatico degli IP
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
data-testid="switch-edit-enabled"
/>
</FormControl>
</FormItem>
)}
/>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => setEditDialogOpen(false)}
data-testid="button-edit-cancel"
>
Annulla
</Button>
<Button
type="submit"
disabled={updateMutation.isPending}
data-testid="button-edit-submit"
>
{updateMutation.isPending ? "Salvataggio..." : "Salva Modifiche"}
</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
</div>
);
}