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