348 lines · 12.5 KB
import { createServerFn } from "@tanstack/react-start";
import { assertBelowCap, CAPS } from "~/server/limits";
import { z } from "zod";
import { viewerMiddleware } from "./middleware";
import { all, first, run } from "~/server/db";
import { randomId, randomToken, sha256Hex } from "~/server/crypto";
import { encryptRepoSecret } from "~/server/secrets";
import { requireRepoAccess } from "~/server/repos";
import { toProtection } from "~/server/protection";
import type { BranchProtectionRow } from "~/server/protection";
import { deliverWebhook, isForgeWebhookUrl } from "~/server/events/consumer";
import { WEBHOOK_EVENTS } from "~/lib/webhook-events";
import type { WebhookEvent } from "~/lib/webhook-events";
import { RUNNER_TOKEN_PREFIX } from "~/server/ci/runner-api";
import { notFoundError } from "~/server/errors";
import { repoRef, secretName } from "~/lib/validation";

export const listProtections = createServerFn({ method: "GET" })
  .middleware([viewerMiddleware])
  .validator(repoRef)
  .handler(async ({ data, context }) => {
    const { repo } = await requireRepoAccess(data.owner, data.repo, context.viewer, "admin");
    const rows = await all<BranchProtectionRow>(
      "SELECT * FROM branch_protections WHERE repo_id = ? ORDER BY pattern",
      repo.id,
    );
    return rows.map(toProtection);
  });

export const saveProtection = createServerFn({ method: "POST" })
  .middleware([viewerMiddleware])
  .validator(
    repoRef.extend({
      id: z.string().nullable(),
      pattern: z.string().trim().min(1).max(200),
      requirePullRequest: z.boolean(),
      requiredApprovals: z.number().int().min(0).max(10),
      requiredChecks: z.array(z.string().min(1)).max(50),
      dismissStaleReviews: z.boolean(),
      allowDeletions: z.boolean(),