feat: enhance audit logging, add MCP settings, and production docker setup
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
- Implemented comprehensive audit logging for Tasks, Users, Settings, Goals, Labels, AI Chat, and Rewards. - Added Admin UI for MCP Server settings and Audit Logs. - Created docker-compose-production.yml with Traefik configuration. - Fixed backend bugs (missing storage methods, route closure). - Added Audit Logging Guidelines.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { format } from "date-fns";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
interface AuditLog {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
action: string;
|
||||
entityType: string;
|
||||
entityId: string | null;
|
||||
source: string;
|
||||
details: any;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function AuditLogsTable() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data: logs, isLoading, error } = useQuery<AuditLog[]>({
|
||||
queryKey: ['/api/admin/audit-logs'],
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="flex justify-center p-8"><Loader2 className="h-8 w-8 animate-spin" /></div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div className="p-8 text-center text-red-500">Failed to load audit logs. Please check server logs.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Audit Logs</CardTitle>
|
||||
<CardDescription>Track all system changes and AI actions.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Time</TableHead>
|
||||
<TableHead>Source</TableHead>
|
||||
<TableHead>Action</TableHead>
|
||||
<TableHead>Entity</TableHead>
|
||||
<TableHead>Details</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{logs?.map((log) => (
|
||||
<TableRow key={log.id}>
|
||||
<TableCell className="whitespace-nowrap">
|
||||
{format(new Date(log.createdAt), "MMM d, HH:mm:ss")}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={log.source === 'AI' ? 'secondary' : 'outline'}>
|
||||
{log.source}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{log.action}</TableCell>
|
||||
<TableCell>
|
||||
{log.entityType}
|
||||
{log.entityId && <span className="text-xs text-muted-foreground block truncate max-w-[100px]">{log.entityId}</span>}
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground w-1/3">
|
||||
<pre className="whitespace-pre-wrap font-mono text-xs">
|
||||
{JSON.stringify(log.details, null, 2)}
|
||||
</pre>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{(!logs || logs.length === 0) && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-8 text-muted-foreground">
|
||||
No logs found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user