"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));
}