93 lines · 3.5 KB
import { createFileRoute, notFound } from "@tanstack/react-router";
import { z } from "zod";
import { getAdminAudit } from "~/fns/admin";
import { Badge } from "~/ui/Badge";
import { EmptyState } from "~/ui/EmptyState";
import { Heading } from "~/ui/Heading";
import { PageContainer } from "~/ui/PageContainer";
import { Panel } from "~/ui/Panel";
import { RelativeTime } from "~/ui/RelativeTime";
import { TextLink } from "~/ui/TextLink";

const PAGE_SIZE = 100;

export const Route = createFileRoute("/admin/audit")({
  head: () => ({ meta: [{ title: "Admin audit log · gitfor" }] }),
  validateSearch: z.object({
    at: z.number().int().nonnegative().optional(),
    id: z.string().min(1).max(64).optional(),
  }),
  beforeLoad: ({ context }) => {
    if (!context.viewer?.isAdmin) throw notFound();
  },
  loaderDeps: ({ search }) => ({ at: search.at, id: search.id }),
  loader: ({ deps }) =>
    getAdminAudit({
      data: { after: deps.at !== undefined && deps.id ? { createdAt: deps.at, id: deps.id } : undefined },
    }),
  component: AdminAuditPage,
});

function AdminAuditPage() {
  const { entries } = Route.useLoaderData();
  const last = entries.at(-1);
  return (
    <PageContainer className="max-w-5xl space-y-4 py-6">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <Heading>Admin audit log</Heading>
        <div className="flex gap-3">
          <TextLink to="/admin/abuse">Abuse queue</TextLink>
          <TextLink to="/admin">Back to site admin</TextLink>