Vectrum
Sign In
50 lines · 1.9 KB · TypeScript
"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { person } from "@/lib/actor";
import { createFork, syncFork } from "@/lib/forks";
import { GitError } from "@/lib/git";
import { ProtectionError } from "@/lib/protection";
import { RepoError } from "@/lib/repos";
import { ActionError, repoForAction } from "./guard";
import type { FormState } from "./types";

// Anyone who can read a repository may fork it; syncing a fork needs push access to the fork.

const isUserError = (e: unknown) => e instanceof ActionError || e instanceof RepoError || e instanceof GitError || e instanceof ProtectionError;

export async function forkRepoAction(owner: string, name: string, _prev: FormState, formData: FormData): Promise<FormState> {
  let destination: string;
  try {
    const { repo, viewer } = await repoForAction(owner, name, "read");
    const fork = await createFork(
      viewer,
      repo,
      {
        name: String(formData.get("name") ?? ""),
        description: String(formData.get("description") ?? ""),
        defaultBranchOnly: formData.get("defaultBranchOnly") === "on",
      },
      person(viewer.id),
    );
    revalidatePath(`/${repo.owner}/${repo.name}`, "layout");
    destination = `/${fork.owner}/${fork.name}`;
  } catch (e) {
    if (isUserError(e)) return { error: (e as Error).message };
    throw e;
  }
  redirect(destination);
}

export async function syncForkAction(owner: string, name: string): Promise<FormState> {
  try {
    const { repo, viewer } = await repoForAction(owner, name, "write");
    const result = await syncFork(repo, person(viewer.id));
    revalidatePath(`/${repo.owner}/${repo.name}`, "layout");
    return { success: result.updated ? "Synced with the upstream repository." : "Already up to date." };
  } catch (e) {
    if (isUserError(e)) return { error: (e as Error).message };
    throw e;
  }
}