Vectrum
Sign In
65 lines · 2.7 KB · TypeScript
"use server";

import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { EVENT_KINDS } from "@/lib/events";
import { createWebhook, deleteWebhook, pingWebhook, updateWebhook, WebhookError } from "@/lib/webhooks";
import { ActionError, repoForAction } from "./guard";
import type { FormState } from "./types";

const hooksPath = (owner: string, name: string) => `/${owner}/${name}/settings/webhooks`;

/** "*" when "all events" is chosen, else the kinds ticked. */
const chosenEvents = (formData: FormData) => (formData.get("which") === "all" ? ["*"] : formData.getAll("events").map(String));

export async function createWebhookAction(owner: string, name: string, _prev: FormState, formData: FormData): Promise<FormState> {
  let id: string;
  try {
    const { repo } = await repoForAction(owner, name, "admin");
    const hook = await createWebhook(repo.id, { url: String(formData.get("url") ?? ""), secret: String(formData.get("secret") ?? ""), events: chosenEvents(formData) }, EVENT_KINDS);
    // A first delivery shows right away whether the receiver is set up.
    await pingWebhook(repo, hook.id).catch(() => {});
    id = hook.id;
  } catch (e) {
    if (e instanceof ActionError || e instanceof WebhookError) return { error: e.message };
    throw e;
  }
  redirect(`${hooksPath(owner, name)}/${id}`);
}

export async function updateWebhookAction(owner: string, name: string, hookId: string, _prev: FormState, formData: FormData): Promise<FormState> {
  try {
    const { repo } = await repoForAction(owner, name, "admin");
    const secret = String(formData.get("secret") ?? "");
    await updateWebhook(
      repo.id,
      hookId,
      {
        url: String(formData.get("url") ?? ""),
        events: chosenEvents(formData),
        active: formData.get("active") === "on",
        // An empty field keeps the secret; "clear" removes it.
        ...(formData.get("clearSecret") === "on" ? { secret: "" } : secret ? { secret } : {}),
      },
      EVENT_KINDS,
    );
    revalidatePath(hooksPath(repo.owner, repo.name), "layout");
    return { success: "Webhook saved." };
  } catch (e) {
    if (e instanceof ActionError || e instanceof WebhookError) return { error: e.message };
    throw e;
  }
}

export async function pingWebhookAction(owner: string, name: string, hookId: string) {
  const { repo } = await repoForAction(owner, name, "admin");
  await pingWebhook(repo, hookId);
  revalidatePath(`${hooksPath(repo.owner, repo.name)}/${hookId}`);
}

export async function deleteWebhookAction(owner: string, name: string, hookId: string) {
  const { repo } = await repoForAction(owner, name, "admin");
  await deleteWebhook(repo.id, hookId);
  revalidatePath(hooksPath(repo.owner, repo.name));
  redirect(hooksPath(repo.owner, repo.name));
}