flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/apps/web/app/routes/workspace/audit-export.ts

33 lines1,630 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Agents get guardrails, run credentials, an audit log, a context hub, repository instructions and mentions; security upkeep; snake_case API1/**
2 * The workspace's audit log as a file: `?format=csv` (the default) or
3 * `json`, with the same filters as the page. Owners get everything;
4 * members what they may see on the page.
5 */
6import { data } from "react-router";
7
8import type { Route } from "./+types/audit-export";
9import { exportName, parseFilters, toCsv, toQuery } from "../../lib/audit";
10import { auditAll } from "../../lib/audit.server";
11import { requireUser } from "../../lib/session.server";
12
13export async function loader({ params, context, request }: Route.LoaderArgs) {
14 const viewer = requireUser(context, request);
15 const url = new URL(request.url);
16 const format = url.searchParams.get("format") === "json" ? "json" : "csv";
17 const workspace = params.owner.toLowerCase();
18 const filters = parseFilters(url.searchParams);
19 // The visibility is decided by auditAll; this one is replaced there.
20 const { visibility: _, ...query } = toQuery(workspace, { kind: "all" }, filters, 500);
21 const entries = await auditAll(viewer, query);
22 if (!entries) throw data("Only members of this workspace can read its audit log.", { status: 403 });
23 const name = exportName(workspace, format, new Date());
24 const headers = {
25 "content-disposition": `attachment; filename="${name}"`,
26 "cache-control": "no-store",
27 };
28 return format === "json"
29 ? new Response(JSON.stringify({ workspace, exportedAt: new Date().toISOString(), entries }, null, 2), {
30 headers: { ...headers, "content-type": "application/json; charset=utf-8" },
31 })
32 : new Response(toCsv(entries), { headers: { ...headers, "content-type": "text/csv; charset=utf-8" } });
33}