38 lines
2.1 KiB
TypeScript
38 lines
2.1 KiB
TypeScript
import { events } from "@minecraft-account-manager/database";
|
|
import { desc } from "drizzle-orm";
|
|
import { db } from "@/lib/database";
|
|
import { eventIpSummary } from "@/lib/event-ip-summary";
|
|
|
|
export default async function EventsPage() {
|
|
const recentEvents = await db.select().from(events).orderBy(desc(events.time)).limit(100);
|
|
|
|
return (
|
|
<main className="mx-auto max-w-6xl px-6 py-14">
|
|
<p className="font-mono text-xs font-bold uppercase tracking-[0.25em] text-accent">CloudEvents ledger</p>
|
|
<h1 className="mt-4 font-display text-5xl font-black uppercase">Recent events</h1>
|
|
<div className="mt-10 overflow-x-auto border border-line bg-panel shadow-[8px_8px_0_var(--color-shadow)]">
|
|
<table className="w-full min-w-[760px] border-collapse text-left">
|
|
<thead className="border-b border-line font-mono text-[10px] uppercase tracking-widest text-muted">
|
|
<tr><th className="p-4">Time</th><th className="p-4">Type</th><th className="p-4">Subject</th><th className="p-4">IP</th><th className="p-4">Network</th></tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-line text-xs">
|
|
{recentEvents.map((event) => {
|
|
const ip = eventIpSummary(event.data);
|
|
return (
|
|
<tr key={event.id}>
|
|
<td className="whitespace-nowrap p-4 font-mono text-muted">{event.time.toISOString()}</td>
|
|
<td className="p-4 font-mono font-bold">{event.type}</td>
|
|
<td className="p-4 font-mono text-muted">{event.subject ?? "—"}</td>
|
|
<td className="p-4 font-mono text-muted">{event.ipAddress ?? "—"}</td>
|
|
<td className="p-4"><div className="font-mono text-[10px] font-bold uppercase">{ip.classification ?? "—"}</div><div className="mt-1 text-xs text-muted">{ip.location ?? "Location unavailable"}</div></td>
|
|
</tr>
|
|
);
|
|
})}
|
|
{!recentEvents.length && <tr><td className="p-8 text-muted" colSpan={5}>No events have been recorded.</td></tr>}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|