39 lines · 2.0 KB
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireViewer, viewerMiddleware } from "./middleware";
import { limitHeavy } from "~/server/limits";
import { assertVerifiedForWrites } from "~/server/verified-writes";
import { requireRepoAccess } from "~/server/repos";
import { compareBranches } from "~/server/git/compare";
import { badRequest, forbidden } from "~/server/errors";
import { loadRepoSettings } from "~/server/mergefix/store";
import { suggestPullDescription as suggest } from "~/server/pull-describe/service";
import { repoRef } from "~/lib/validation";

/** Billed to the viewer (their plan or their own key), never the repository owner: any reader can open a pull request. */
export const suggestPullDescription = createServerFn({ method: "POST" })
  .middleware([viewerMiddleware])
  .validator(repoRef.extend({ base: z.string().min(1), head: z.string().min(1) }))
  .handler(async ({ data, context }) => {
    const viewer = requireViewer(context.viewer);
    assertVerifiedForWrites(viewer);
    await limitHeavy(viewer);
    const { repo } = await requireRepoAccess(data.owner, data.repo, viewer, "read");
    // Private code only goes to a model when the repository opted in (the same switch as merge fixes).
    if (repo.visibility !== "public" && !(await loadRepoSettings(repo.id)).allowPrivate)
      throw forbidden("AI features are off for this private repository");
    const compared = await compareBranches(repo, data.base, data.head, viewer);
    const headRepo = compared.headRepo;
    if (
      headRepo.id !== repo.id &&
      headRepo.visibility !== "public" &&
      !(await loadRepoSettings(headRepo.id)).allowPrivate
    )
      throw forbidden("AI features are off for the private head repository");
    if (compared.comparison.commits.length === 0) throw badRequest("There are no commits to describe");
    return suggest(
      { accountId: viewer.id, repoId: repo.id },
      { base: compared.base.name, head: compared.head.name, commits: compared.comparison.commits, diff: compared.diff },
    );
  });