import { env } from "cloudflare:workers";
import { z } from "zod";
import { cookieAttributes, cookieName, cookieValue, viewerFromSessionCookie } from "../auth";
import { randomToken, timingSafeEqualStrings } from "../crypto";
import { DEFAULT_INTEGRATION_EVENTS, DEFAULT_FILTERS, DEFAULT_LINEAR_MAPPING } from "~/lib/integrations";
import { isSuccess, outboundFetch } from "./outbound";
import { linearGraphql } from "./linear";
import { requireIntegrationScope } from "./scope";
import type { ResolvedScope } from "./scope";
import { insertIntegration } from "./store";
import { publicUrl } from "../events/emit";
const STATE_TTL_SECONDS = 600;
const STATE_COOKIE = "gitfor_integration_oauth";
type Provider = "slack" | "linear" | "github";
type PendingInstall = { provider: Provider; viewerId: string; scope: ResolvedScope };
function callbackUrl(provider: Provider): string {
return publicUrl(`/integrations/${provider}/callback`);
}
function redirect(location: string, cookie?: string): Response {
const headers: Record<string, string> = { location };
if (cookie) headers["set-cookie"] = cookie;
return new Response(null, { status: 302, headers });
}
function failure(message: string, status = 400): Response {
return new Response(`${message}\n`, { status, headers: { "content-type": "text/plain; charset=utf-8" } });
}
/** Starts an install: checks the viewer administers the scope, then binds a one-time state to KV and a cookie. */
async function begin(request: Request, provider: Provider, authorizeUrl: (state: string) => string | null) {
const url = new URL(request.url);
const viewer = await viewerFromSessionCookie(request);
const scope = await requireIntegrationScope(
{ owner: url.searchParams.get("owner") ?? "", repo: url.searchParams.get("repo") || null },
viewer,
);