127 lines · 6.1 KB
import { waitUntil } from "cloudflare:workers";
import { all, batch, stmt } from "./db";
import { loadUserById, toViewer } from "./auth";
import { pullHeadAccessible } from "./git/bridge-policy";
import { dropHiddenRefsForRepo, dropPullRefsForIssue, requestHiddenRefCleanup } from "./git/pull-bridge";
import { CAPS } from "./limits";
import { addIssueEvent } from "./pulls";
import type { PullRow } from "./pulls";
import { hasPermission, loadRepoById, repoPermission } from "./repos";
import type { RepoRow, Viewer } from "~/lib/types";

type GuardedPull = Pick<PullRow, "issue_id" | "repo_id" | "head_repo_id" | "author_id" | "state">;

/** Closes a pull request because its author can no longer read one of its repositories. */
async function closeForRevokedAccess(pull: GuardedPull): Promise<void> {
  const now = Date.now();
  await batch([
    stmt(
      "UPDATE issues SET state = 'closed', closed_at = ?, closed_by = NULL, updated_at = ? WHERE id = ? AND state = 'open'",
      now,
      now,
      pull.issue_id,
    ),
    stmt("UPDATE repos SET open_pulls_count = MAX(open_pulls_count - 1, 0) WHERE id = ?", pull.repo_id),
  ]);
  await addIssueEvent(pull.issue_id, null, "closed", { reason: "access_revoked" });
  waitUntil(dropPullRefsForIssue(pull.issue_id));
}

/**
 * Whether a fork pull request may still synchronize, bridge or run CI: the author must currently read
 * both repositories and the head must be a live fork of the base. When that stopped being true the pull
 * request is closed and its hidden refs dropped, so revoked readers stop receiving (and feeding) history.
 */
export async function ensurePullHeadAccessible(pull: GuardedPull): Promise<boolean> {
  if (pull.repo_id === pull.head_repo_id) return true;
  const [baseRepo, headRepo] = await Promise.all([loadRepoById(pull.repo_id), loadRepoById(pull.head_repo_id)]);
  if (!baseRepo || !headRepo) return false;
  if (await pullHeadAccessible(pull, baseRepo, headRepo)) return true;
  if (pull.state === "open") await closeForRevokedAccess(pull);