feat: 支持证书托管和节点自动安装
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"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<unknown>) {
|
||||
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"),
|
||||
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"),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user