feat: 支持证书托管和节点自动安装
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { requireAdminAccess } from "@/lib/session";
|
||||
import { installationForDownload } from "@/lib/provisioning";
|
||||
import { buildInstallScript } from "@/lib/install-script";
|
||||
import { ProvisionError } from "@/lib/certificates";
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
context: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
await requireAdminAccess();
|
||||
try {
|
||||
const job = await installationForDownload((await context.params).id);
|
||||
return new Response(buildInstallScript(job), {
|
||||
headers: {
|
||||
"Content-Type": "text/x-shellscript; charset=utf-8",
|
||||
"Content-Disposition": `attachment; filename="install-${job.slug}.sh"`,
|
||||
"Cache-Control": "no-store",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (!(error instanceof ProvisionError)) throw error;
|
||||
return new Response(error.message, {
|
||||
status: 410,
|
||||
headers: { "Cache-Control": "no-store" },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ProvisionError } from "@/lib/certificates";
|
||||
import { provisionNode } from "@/lib/provisioning";
|
||||
export const runtime = "nodejs";
|
||||
export async function POST(request: Request) {
|
||||
const headers = { "Cache-Control": "no-store" };
|
||||
try {
|
||||
// Bound streamed input, including requests without Content-Length.
|
||||
const reader = request.body?.getReader();
|
||||
if (!reader)
|
||||
return Response.json(
|
||||
{ error: "缺少请求内容。" },
|
||||
{ status: 400, headers },
|
||||
);
|
||||
const chunks: Uint8Array[] = [];
|
||||
let size = 0;
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
size += value.byteLength;
|
||||
if (size > 4096) {
|
||||
await reader.cancel();
|
||||
return Response.json({ error: "请求过大。" }, { status: 413, headers });
|
||||
}
|
||||
chunks.push(value);
|
||||
}
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
||||
} catch {
|
||||
return Response.json({ error: "无效 JSON。" }, { status: 400, headers });
|
||||
}
|
||||
if (!payload || !["prepare", "complete"].includes(payload.action))
|
||||
return Response.json({ error: "无效操作。" }, { status: 400, headers });
|
||||
const token =
|
||||
request.headers.get("authorization")?.replace(/^Bearer /, "") ?? "";
|
||||
return Response.json(
|
||||
await provisionNode(token, payload.action, payload.ports),
|
||||
{ headers },
|
||||
);
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{
|
||||
error:
|
||||
error instanceof ProvisionError
|
||||
? error.message
|
||||
: "安装请求失败,请联系平台管理员。",
|
||||
},
|
||||
{ status: error instanceof ProvisionError ? 400 : 500, headers },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user