feat: Fleet Forge UEM - device management, agent, remote desktop (VNC), TLS reverse proxy
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { api } from "@/lib/api";
|
||||
import DashboardLayout from "@/components/DashboardLayout";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { ScrollText } from "lucide-react";
|
||||
|
||||
interface AuditEvent {
|
||||
id: string;
|
||||
action: string;
|
||||
targetType: string;
|
||||
targetId: string | null;
|
||||
result: string;
|
||||
ipAddress: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
correlationId: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export default function AuditLogPage() {
|
||||
const [events, setEvents] = useState<AuditEvent[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [actionFilter, setActionFilter] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const params: Record<string, string> = {};
|
||||
if (actionFilter) params.action = actionFilter;
|
||||
api.get<AuditEvent[]>("/v1/audit-events", params).then((res) => {
|
||||
if (res.success && res.data) {
|
||||
setEvents(res.data);
|
||||
setTotal(res.meta?.total || 0);
|
||||
}
|
||||
});
|
||||
}, [actionFilter]);
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold tracking-tight">Audit Log</h1>
|
||||
<p className="text-muted-foreground">{total} event(s) recorded</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<Input
|
||||
placeholder="Filter by action..."
|
||||
value={actionFilter}
|
||||
onChange={(e) => setActionFilter(e.target.value)}
|
||||
className="w-64"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{events.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="p-12 text-center">
|
||||
<ScrollText className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">No audit events recorded yet.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Time</TableHead>
|
||||
<TableHead>Action</TableHead>
|
||||
<TableHead>Target</TableHead>
|
||||
<TableHead>Result</TableHead>
|
||||
<TableHead>IP</TableHead>
|
||||
<TableHead>Correlation ID</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{events.map((event) => (
|
||||
<TableRow key={event.id}>
|
||||
<TableCell className="text-xs text-muted-foreground">
|
||||
{new Date(event.createdAt).toLocaleString()}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm font-medium">
|
||||
{event.action}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">
|
||||
{event.targetType}
|
||||
{event.targetId && (
|
||||
<span className="ml-1">
|
||||
({event.targetId.substring(0, 8)}...)
|
||||
</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={event.result === "success" ? "success" : "destructive"}>
|
||||
{event.result}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground">
|
||||
{event.ipAddress || "\u2014"}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground font-mono">
|
||||
{event.correlationId.substring(0, 12)}...
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
)}
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user