Skip to content

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.sh
54 linesCodeBlameRaw
1import { redirect } from "react-router";
2
3import { isAiProvider, type Provider, PROVIDERS } from "@g1t/contracts";
4import { connectorsFor } from "@g1t/contracts/connectors";
5
6import type { Route } from "./+types/integrations-directory";
7import { ConnectorDirectory, ScopeNote } from "../../components/connectors";
8import { connectedStates } from "../../lib/connected.server";
9import { aiPagePath, providerPage } from "../../lib/integration-sections";
10import { page } from "../../lib/meta";
11import { requireUser, roleIn } from "../../lib/session.server";
12
13export function meta({ params, ...args }: Route.MetaArgs) {
14 return page(args, { title: `Integrations · ${params.owner} · g1t` });
15}
16
17/**
18 * The workspace's integrations: every connector g1t has or will have, by
19 * category, with what this workspace has connected first. Each one is set
20 * up on its own page (the alerts and trackers pages, the GitHub import,
21 * webhooks); this page only finds them. AI providers are not integrations:
22 * they are the workspace's AI settings, at `-/ai`.
23 */
24export async function loader({ params, context, request }: Route.LoaderArgs) {
25 // Signed out, sign in first (as Chat and Docs do), rather than a 404.
26 const viewer = requireUser(context, request);
27 const role = roleIn(viewer, params.owner);
28 if (!role || !viewer) throw new Response(null, { status: 404 });
29 const slug = params.owner.toLowerCase();
30
31 // An older link to one provider's form (`?add=sentry`): its own page
32 // now, and the AI page for an AI provider (`?add=anthropic`).
33 const add = new URL(request.url).searchParams.get("add");
34 if (add && isAiProvider(add)) throw redirect(aiPagePath(slug, `?add=${add}`));
35 if (add && add in PROVIDERS) throw redirect(`/${slug}/-/${providerPage(PROVIDERS[add as Provider].kind)}?add=${add}#add`);
36
37 return { slug, owner: role === "owner", connected: await connectedStates(slug, viewer) };
38}
39
40export default function WorkspaceIntegrationsDirectory({ loaderData }: Route.ComponentProps) {
41 const { slug, owner, connected } = loaderData;
42 return (
43 <div>
44 <ScopeNote scope="workspace" workspace={slug} />
45 <ConnectorDirectory
46 scope="workspace"
47 views={connectorsFor("workspace")}
48 connected={connected}
49 workspace={slug}
50 canManage={owner}
51 />
52 </div>
53 );
54}