159 lines · 7.2 KB
import { env } from "cloudflare:workers";
import { all } from "../db";
import { enforceRateLimit, requestIp } from "../limits";
import { loadRepo } from "../repos";
import { publicUrl } from "../events/emit";
import { enqueueDirect, enqueueIntegrationDispatch } from "./queue";
import { verifyLinearSignature, verifySha256Signature, verifySlackSignature } from "./signatures";
import { loadIntegration, openCredentials, parseConfig, recordDelivery } from "./store";
import type { IntegrationRow } from "./store";
import { escapeSlack } from "./format";

export const INBOUND_HOOK_PREFIX = "/integrations/hooks/";

const MAX_INBOUND_BYTES = 1024 * 1024;
const MAX_LISTED_PULLS = 5;
const REPO_ARG = /^([A-Za-z0-9][A-Za-z0-9-]{0,38})\/([A-Za-z0-9._-]{1,100})$/;

async function readBody(request: Request): Promise<string | null> {
  if (Number(request.headers.get("content-length") ?? "0") > MAX_INBOUND_BYTES) return null;
  const text = await request.text();
  return text.length > MAX_INBOUND_BYTES ? null : text;
}

function slackReply(text: string): Response {
  return Response.json({ response_type: "ephemeral", text });
}

const SLACK_HELP = "Usage: `/gitfor status owner/repo` shows open pull requests and issues. `/gitfor help` shows this.";

/** A repo is visible to a Slack workspace when it is public or has a Slack integration for that workspace. */
async function slackCanSee(teamId: string, repoId: string, ownerId: string, visibility: string): Promise<boolean> {
  if (visibility === "public") return true;
  const rows = await all<IntegrationRow>(
    `SELECT * FROM integrations WHERE kind = 'slack' AND active = 1
     AND ((scope_type = 'repo' AND scope_id = ?) OR (scope_type = 'org' AND scope_id = ?)) LIMIT 20`,
    repoId,
    ownerId,
  );
  return rows.some((row) => parseConfig(row).slackTeam === teamId);
}