feat: Implement API client and dashboard functionality
- Added api.ts to handle API requests and define data models for Project, Image, Vulnerability, IgnoreRule, ScanJob, and DashboardStats. - Created Dashboard component to display statistics and initiate scans for projects and vulnerabilities. - Developed IgnoreRules component for managing ignore rules with filtering options. - Implemented Images component to list discovered Docker images. - Added Projects component to display monitored GitLab projects. - Created ScanJobs component to show history and status of scanning operations. - Developed Vulnerabilities component to report security vulnerabilities found in Docker images. - Removed BrowserRouter from main.tsx as routing is not currently implemented.
This commit is contained in:
181
src/pages/IgnoreRules.tsx
Normal file
181
src/pages/IgnoreRules.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
||||
import { apiClient, IgnoreRule } from '@/lib/api'
|
||||
import { Settings, RefreshCw, Plus, Trash2 } from 'lucide-react'
|
||||
|
||||
const ignoreTypeColors = {
|
||||
image: 'default',
|
||||
file: 'secondary',
|
||||
project: 'outline',
|
||||
} as const
|
||||
|
||||
export function IgnoreRules() {
|
||||
const [ignoreRules, setIgnoreRules] = useState<IgnoreRule[]>([])
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [selectedType, setSelectedType] = useState<string>('')
|
||||
|
||||
const fetchIgnoreRules = async (ignoreType?: string) => {
|
||||
try {
|
||||
const data = await apiClient.getIgnoreRules({
|
||||
ignore_type: ignoreType || undefined
|
||||
})
|
||||
setIgnoreRules(data)
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch ignore rules:', error)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteRule = async (id: number) => {
|
||||
try {
|
||||
await apiClient.deleteIgnoreRule(id)
|
||||
await fetchIgnoreRules(selectedType)
|
||||
} catch (error) {
|
||||
console.error('Failed to delete ignore rule:', error)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchIgnoreRules(selectedType)
|
||||
}, [selectedType])
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<RefreshCw className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Ignore Rules</h1>
|
||||
<p className="text-gray-600">
|
||||
Manage rules to exclude projects, files, or images from scanning
|
||||
</p>
|
||||
</div>
|
||||
<Button>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Add Rule
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Type Filter */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Filter by Type</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => setSelectedType('')}
|
||||
className={`px-3 py-1 rounded text-sm ${
|
||||
selectedType === ''
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
{Object.keys(ignoreTypeColors).map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
onClick={() => setSelectedType(type)}
|
||||
className={`px-3 py-1 rounded text-sm ${
|
||||
selectedType === type
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<Badge variant={ignoreTypeColors[type as keyof typeof ignoreTypeColors]}>
|
||||
{type.charAt(0).toUpperCase() + type.slice(1)}
|
||||
</Badge>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center">
|
||||
<Settings className="h-5 w-5 mr-2" />
|
||||
Active Ignore Rules
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Rules that exclude items from scanning and vulnerability checking
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead>Target</TableHead>
|
||||
<TableHead>Reason</TableHead>
|
||||
<TableHead>Created By</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead>Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{ignoreRules.map((rule) => (
|
||||
<TableRow key={rule.id}>
|
||||
<TableCell>
|
||||
<Badge variant={ignoreTypeColors[rule.ignore_type as keyof typeof ignoreTypeColors]}>
|
||||
{rule.ignore_type.charAt(0).toUpperCase() + rule.ignore_type.slice(1)}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="font-mono text-sm max-w-md break-words">
|
||||
{rule.target}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="max-w-md">
|
||||
{rule.reason || (
|
||||
<span className="text-gray-400 italic">No reason provided</span>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{rule.created_by || (
|
||||
<span className="text-gray-400">Unknown</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-gray-600">
|
||||
{new Date(rule.created_at).toLocaleDateString()}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handleDeleteRule(rule.id)}
|
||||
className="text-red-600 hover:text-red-800"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{ignoreRules.length === 0 && (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
{selectedType
|
||||
? `No ${selectedType} ignore rules found.`
|
||||
: 'No ignore rules configured. Add rules to exclude items from scanning.'
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user