Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/prompts/autonomy-version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"version": "2025.11"
}
8 changes: 8 additions & 0 deletions .github/prompts/autonomy.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"version": "2025.11",
"consent": {
"phrase": "",
"expiresMinutes": 0
},
"actions": []
}
80 changes: 80 additions & 0 deletions frontend/app/(protected)/admin/activity/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
import { CircleUser } from "lucide-react";

// Define the type for the activity data
interface Activity {
id: string;
user_email: string;
action: string;
created_at: string;
}

async function getActivity(): Promise<Activity[]> {
// This is a server component, so we can fetch data directly.
// In a real app, you'd fetch from your API endpoint.
// The URL should be absolute, e.g., process.env.API_URL.
// For this example, we'll use mock data.
try {
// const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/activity`);
// if (!res.ok) {
// throw new Error('Failed to fetch activity data');
// }
// const data = await res.json();
// return data;

// Mock data for demonstration
const mockData: Activity[] = [
{ id: '1', user_email: 'admin@example.com', action: 'User login', created_at: new Date().toISOString() },
{ id: '2', user_email: 'test@example.com', action: 'File upload: "document.pdf"', created_at: new Date(Date.now() - 1000 * 60 * 5).toISOString() },
{ id: '3', user_email: 'admin@example.com', action: 'File delete: "image.jpg"', created_at: new Date(Date.now() - 1000 * 60 * 10).toISOString() },
{ id: '4', user_email: 'guest@example.com', action: 'Viewed page: "Dashboard"', created_at: new Date(Date.now() - 1000 * 60 * 15).toISOString() },
];
return mockData;
} catch (error) {
console.error(error);
return [];
}
}

export default async function AdminActivityPage() {
const activities = await getActivity();

return (
<div className="p-4 md:p-8">
<h1 className="text-2xl font-bold mb-4">User Activity Timeline</h1>
<Card>
<CardHeader>
<CardTitle>Activity Feed</CardTitle>
<CardDescription>
An overview of recent user actions on the platform.
</CardDescription>
</CardHeader>
<CardContent>
<div className="relative pl-6">
{/* The timeline line */}
<div className="absolute left-6 top-0 bottom-0 w-0.5 bg-gray-200"></div>

{activities.map((activity, index) => (
<div key={activity.id} className="mb-8 pl-8 relative">
{/* The timeline dot */}
<div className="absolute left-[-4px] top-1 w-4 h-4 bg-blue-500 rounded-full border-4 border-white"></div>

<div className="flex items-center mb-1">
<CircleUser className="w-5 h-5 mr-2" />
<p className="font-semibold">{activity.user_email}</p>
</div>
<p className="text-sm text-gray-600">{activity.action}</p>
<p className="text-xs text-gray-400 mt-1">
{new Date(activity.created_at).toLocaleString()}
</p>
</div>
))}
{activities.length === 0 && (
<p>No recent activity.</p>
)}
</div>
</CardContent>
</Card>
</div>
);
}
90 changes: 90 additions & 0 deletions frontend/app/(protected)/admin/audit-logs/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table";

// Define the type for the audit log data
interface AuditLog {
id: string;
ip_address: string;
timestamp: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
path: string;
status_code: number;
user_agent: string;
}

async function getAuditLogs(): Promise<AuditLog[]> {
// This is a server component, so we can fetch data directly.
// In a real app, you'd fetch from your API endpoint.
// For this example, we'll use mock data.
try {
// const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/audit`);
// if (!res.ok) {
// throw new Error('Failed to fetch audit logs');
// }
// const data = await res.json();
// return data;

// Mock data for demonstration
const mockData: AuditLog[] = [
{ id: '1', ip_address: '192.168.1.1', timestamp: new Date().toISOString(), method: 'GET', path: '/api/users', status_code: 200, user_agent: 'Mozilla/5.0' },
{ id: '2', ip_address: '10.0.0.1', timestamp: new Date(Date.now() - 1000 * 60 * 2).toISOString(), method: 'POST', path: '/api/auth/login', status_code: 200, user_agent: 'Chrome/91.0' },
{ id: '3', ip_address: '172.16.0.1', timestamp: new Date(Date.now() - 1000 * 60 * 5).toISOString(), method: 'GET', path: '/api/files/123', status_code: 404, user_agent: 'curl/7.64.1' },
{ id: '4', ip_address: '192.168.1.2', timestamp: new Date(Date.now() - 1000 * 60 * 10).toISOString(), method: 'DELETE', path: '/api/files/456', status_code: 204, user_agent: 'Mozilla/5.0' },
];
return mockData;
} catch (error) {
console.error(error);
return [];
}
}

export default async function AdminAuditLogsPage() {
const auditLogs = await getAuditLogs();

return (
<div className="p-4 md:p-8">
<h1 className="text-2xl font-bold mb-4">HTTP Access Logs</h1>
<Card>
<CardHeader>
<CardTitle>Audit Logs</CardTitle>
<CardDescription>
A record of all HTTP requests made to the server.
</CardDescription>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>IP Address</TableHead>
<TableHead>Timestamp</TableHead>
<TableHead>Method</TableHead>
<TableHead>Path</TableHead>
<TableHead>Status</TableHead>
<TableHead>User Agent</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{auditLogs.map((log) => (
<TableRow key={log.id}>
<TableCell>{log.ip_address}</TableCell>
<TableCell>{new Date(log.timestamp).toLocaleString()}</TableCell>
<TableCell>{log.method}</TableCell>
<TableCell>{log.path}</TableCell>
<TableCell>{log.status_code}</TableCell>
<TableCell>{log.user_agent}</TableCell>
</TableRow>
))}
{auditLogs.length === 0 && (
<TableRow>
<TableCell colSpan={6} className="text-center">
No audit logs found.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</CardContent>
</Card>
</div>
);
}
Loading