"use server"; import { revalidatePath } from "next/cache"; import { requireAdminAccess } from "@/lib/session"; import { saveCertificate, deleteCertificate, ProvisionError, } from "@/lib/certificates"; import { createInstallation, installationSchema, revokeInstallation, } from "@/lib/provisioning"; async function run(action: () => Promise) { await requireAdminAccess(); try { await action(); } catch (error) { if (error instanceof ProvisionError) return { error: error.message }; throw error; } revalidatePath("/admin/certificates"); revalidatePath("/admin/nodes"); } export async function uploadCertificateAction(data: FormData) { return run(async () => { const crt = data.get("certificate"), key = data.get("private_key"); if ( !(crt instanceof File) || !(key instanceof File) || crt.size > 128 * 1024 || key.size > 32 * 1024 ) throw new ProvisionError( "请选择 PEM 格式的 CRT(最多 128 KB)和 KEY(最多 32 KB)文件。", ); await saveCertificate( String(data.get("name") ?? ""), await crt.text(), await key.text(), ); }); } export async function deleteCertificateAction(data: FormData) { return run(() => deleteCertificate(Number(data.get("id")))); } export async function createInstallationAction(data: FormData) { return run(async () => { const input = installationSchema.safeParse({ name: data.get("name"), protocol: data.get("protocol") || "hy2", slug: data.get("slug"), certificateId: data.get("certificate_id"), }); if (!input.success) throw new ProvisionError( "请检查输入:Slug 使用小写字母、数字或连字符,并选择覆盖 *.p.xn--876a.net 的证书。", ); await createInstallation(input.data); }); } export async function revokeInstallationAction(data: FormData) { return run(() => revokeInstallation(String(data.get("id")), data.get("remove") === "yes"), ); }