feat: 支持托管中转主机和节点接入线路
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { requireAdminAccess } from "@/lib/session";
|
||||
import { relayInventory } from "@/lib/relays";
|
||||
import { decryptText } from "@/lib/crypto";
|
||||
import { getEnv } from "@/lib/env";
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
context: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
await requireAdminAccess();
|
||||
const id = (await context.params).id;
|
||||
const row = (await relayInventory()).hosts.find((h) => h.id === id);
|
||||
if (!row) return new Response(null, { status: 404 });
|
||||
const origin = new URL(getEnv().APP_URL);
|
||||
if (
|
||||
origin.protocol !== "https:" &&
|
||||
!["localhost", "127.0.0.1"].includes(origin.hostname)
|
||||
)
|
||||
return new Response("请配置 HTTPS APP_URL。", { status: 409 });
|
||||
const source = await readFile(
|
||||
path.join(process.cwd(), "lib/templates/relay-agent.py"),
|
||||
"utf8",
|
||||
);
|
||||
const token = decryptText(row.token_encrypted);
|
||||
const payload = Buffer.from(
|
||||
JSON.stringify({
|
||||
credentials: { panel: origin.origin, id, token },
|
||||
source,
|
||||
}),
|
||||
).toString("base64");
|
||||
const script = (
|
||||
await readFile(
|
||||
path.join(process.cwd(), "lib/templates/install-relay.sh"),
|
||||
"utf8",
|
||||
)
|
||||
)
|
||||
.replace("__ID__", `'${id}'`)
|
||||
.replace("__PAYLOAD__", `'${payload}'`);
|
||||
return new Response(script, {
|
||||
headers: {
|
||||
"Content-Type": "text/x-shellscript; charset=utf-8",
|
||||
"Content-Disposition": `attachment; filename="install-relay-${id}.sh"`,
|
||||
"Cache-Control": "no-store",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { syncRelay, RelayError } from "@/lib/relays";
|
||||
export const runtime = "nodejs";
|
||||
export async function POST(
|
||||
request: Request,
|
||||
context: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
const headers = { "Cache-Control": "no-store" };
|
||||
try {
|
||||
const reader = request.body?.getReader();
|
||||
if (!reader) return new Response(null, { status: 400, headers });
|
||||
const chunks: Uint8Array[] = [];
|
||||
let size = 0;
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
size += value.byteLength;
|
||||
if (size > 16384) {
|
||||
await reader.cancel();
|
||||
return new Response(null, { status: 413, headers });
|
||||
}
|
||||
chunks.push(value);
|
||||
}
|
||||
return Response.json(
|
||||
await syncRelay(
|
||||
(await context.params).id,
|
||||
request.headers.get("authorization")?.replace(/^Bearer /, "") || "",
|
||||
JSON.parse(Buffer.concat(chunks).toString("utf8")),
|
||||
),
|
||||
{ headers },
|
||||
);
|
||||
} catch (e) {
|
||||
return Response.json(
|
||||
{ error: e instanceof RelayError ? e.message : "无效报告。" },
|
||||
{ status: e instanceof RelayError ? 403 : 400, headers },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user