Compare commits

...

2 Commits

Author SHA1 Message Date
Marco Lanzara
58fb6476c5 🚀 Release v1.0.99
- Tipo: patch
- Database schema: database-schema/schema.sql (solo struttura)
- Data: 2026-01-02 15:39:39
2026-01-02 15:39:39 +00:00
marco370
1b47e08129 Add search functionality to the whitelist page and improve IP status indication
Add a search bar to the whitelist page and filter results by IP, reason, and notes. Modify the detections page to visually indicate when an IP is already whitelisted by changing the button color to green and using a different icon.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 7a657272-55ba-4a79-9a2e-f1ed9bc7a528
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4231475f-0a12-42cd-bf3f-3401022fd4e5
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/449cf7c4-c97a-45ae-8234-e5c5b8d6a84f/7a657272-55ba-4a79-9a2e-f1ed9bc7a528/8i4FqXF
2026-01-02 15:37:32 +00:00
4 changed files with 72 additions and 27 deletions

View File

@ -5,10 +5,10 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Slider } from "@/components/ui/slider";
import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus } from "lucide-react";
import { AlertTriangle, Search, Shield, Globe, MapPin, Building2, ShieldPlus, ShieldCheck } from "lucide-react";
import { format } from "date-fns";
import { useState } from "react";
import type { Detection } from "@shared/schema";
import type { Detection, Whitelist } from "@shared/schema";
import { getFlag } from "@/lib/country-flags";
import { apiRequest, queryClient } from "@/lib/queryClient";
import { useToast } from "@/hooks/use-toast";
@ -39,6 +39,14 @@ export default function Detections() {
refetchInterval: 5000,
});
// Fetch whitelist to check if IP is already whitelisted
const { data: whitelistData } = useQuery<Whitelist[]>({
queryKey: ["/api/whitelist"],
});
// Create a Set of whitelisted IPs for fast lookup
const whitelistedIps = new Set(whitelistData?.map(w => w.ipAddress) || []);
const filteredDetections = detections?.filter((d) =>
d.sourceIp.toLowerCase().includes(searchQuery.toLowerCase()) ||
d.anomalyType.toLowerCase().includes(searchQuery.toLowerCase())
@ -278,6 +286,18 @@ export default function Detections() {
</Badge>
)}
{whitelistedIps.has(detection.sourceIp) ? (
<Button
variant="outline"
size="sm"
disabled
className="w-full bg-green-500/10 border-green-500 text-green-600 dark:text-green-400"
data-testid={`button-whitelist-${detection.id}`}
>
<ShieldCheck className="h-3 w-3 mr-1" />
In Whitelist
</Button>
) : (
<Button
variant="outline"
size="sm"
@ -289,6 +309,7 @@ export default function Detections() {
<ShieldPlus className="h-3 w-3 mr-1" />
Whitelist
</Button>
)}
</div>
</div>
</div>

View File

@ -2,7 +2,7 @@ import { useQuery, useMutation } from "@tanstack/react-query";
import { queryClient, apiRequest } from "@/lib/queryClient";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Shield, Plus, Trash2, CheckCircle2, XCircle } from "lucide-react";
import { Shield, Plus, Trash2, CheckCircle2, XCircle, Search } from "lucide-react";
import { format } from "date-fns";
import { useState } from "react";
import { useForm } from "react-hook-form";
@ -44,6 +44,7 @@ const whitelistFormSchema = insertWhitelistSchema.extend({
export default function WhitelistPage() {
const { toast } = useToast();
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const form = useForm<z.infer<typeof whitelistFormSchema>>({
resolver: zodResolver(whitelistFormSchema),
@ -59,6 +60,13 @@ export default function WhitelistPage() {
queryKey: ["/api/whitelist"],
});
// Filter whitelist based on search query
const filteredWhitelist = whitelist?.filter((item) =>
item.ipAddress.toLowerCase().includes(searchQuery.toLowerCase()) ||
item.reason?.toLowerCase().includes(searchQuery.toLowerCase()) ||
item.comment?.toLowerCase().includes(searchQuery.toLowerCase())
);
const addMutation = useMutation({
mutationFn: async (data: z.infer<typeof whitelistFormSchema>) => {
return await apiRequest("POST", "/api/whitelist", data);
@ -189,11 +197,27 @@ export default function WhitelistPage() {
</Dialog>
</div>
{/* Search Bar */}
<Card data-testid="card-search">
<CardContent className="pt-6">
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Cerca per IP, motivo o note..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9"
data-testid="input-search-whitelist"
/>
</div>
</CardContent>
</Card>
<Card data-testid="card-whitelist">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="h-5 w-5" />
IP Protetti ({whitelist?.length || 0})
IP Protetti ({filteredWhitelist?.length || 0}{searchQuery && whitelist ? ` di ${whitelist.length}` : ''})
</CardTitle>
</CardHeader>
<CardContent>
@ -201,9 +225,9 @@ export default function WhitelistPage() {
<div className="text-center py-8 text-muted-foreground" data-testid="text-loading">
Caricamento...
</div>
) : whitelist && whitelist.length > 0 ? (
) : filteredWhitelist && filteredWhitelist.length > 0 ? (
<div className="space-y-3">
{whitelist.map((item) => (
{filteredWhitelist.map((item) => (
<div
key={item.id}
className="p-4 rounded-lg border hover-elevate"

View File

@ -2,7 +2,7 @@
-- PostgreSQL database dump
--
\restrict Z0SMaiaV5vhgZwK1NSwbNjjsNLFygVnbAXhqZs1XJQSNOdt4n4ybTuKWgXktCsc
\restrict PRKBLjzmAC8I39HJVa9aOlkzFFiqgPPqt4hjKaZLwxRVM51Z47YCL9xNIeoXWQj
-- Dumped from database version 16.11 (74c6bb6)
-- Dumped by pg_dump version 16.10
@ -387,5 +387,5 @@ ALTER TABLE ONLY public.public_blacklist_ips
-- PostgreSQL database dump complete
--
\unrestrict Z0SMaiaV5vhgZwK1NSwbNjjsNLFygVnbAXhqZs1XJQSNOdt4n4ybTuKWgXktCsc
\unrestrict PRKBLjzmAC8I39HJVa9aOlkzFFiqgPPqt4hjKaZLwxRVM51Z47YCL9xNIeoXWQj

View File

@ -1,7 +1,13 @@
{
"version": "1.0.98",
"lastUpdate": "2026-01-02T15:20:02.824Z",
"version": "1.0.99",
"lastUpdate": "2026-01-02T15:39:39.640Z",
"changelog": [
{
"version": "1.0.99",
"date": "2026-01-02",
"type": "patch",
"description": "Deployment automatico v1.0.99"
},
{
"version": "1.0.98",
"date": "2026-01-02",
@ -295,12 +301,6 @@
"date": "2025-11-24",
"type": "patch",
"description": "Deployment automatico v1.0.50"
},
{
"version": "1.0.49",
"date": "2025-11-24",
"type": "patch",
"description": "Deployment automatico v1.0.49"
}
]
}