Spaces:
Sleeping
Sleeping
| import { NextResponse } from "next/server"; | |
| import { createProxyPool } from "@/models"; | |
| // Relay worker source code deployed to Cloudflare | |
| const RELAY_WORKER_CODE = ` | |
| export default { | |
| async fetch(request, env, ctx) { | |
| const target = request.headers.get("x-relay-target"); | |
| const relayPath = request.headers.get("x-relay-path") || "/"; | |
| if (!target) { | |
| return new Response(JSON.stringify({ error: "Missing x-relay-target header" }), { | |
| status: 400, | |
| headers: { "content-type": "application/json" }, | |
| }); | |
| } | |
| const targetUrl = target.replace(/\\/$/, "") + relayPath; | |
| const newRequestInit = { | |
| method: request.method, | |
| headers: new Headers(request.headers), | |
| }; | |
| if (request.method !== "GET" && request.method !== "HEAD") { | |
| newRequestInit.body = request.body; | |
| newRequestInit.duplex = "half"; | |
| } | |
| newRequestInit.headers.delete("x-relay-target"); | |
| newRequestInit.headers.delete("x-relay-path"); | |
| newRequestInit.headers.delete("host"); | |
| try { | |
| const response = await fetch(targetUrl, newRequestInit); | |
| return new Response(response.body, { | |
| status: response.status, | |
| headers: response.headers, | |
| }); | |
| } catch (error) { | |
| return new Response(JSON.stringify({ error: error.message }), { | |
| status: 502, | |
| headers: { "content-type": "application/json" }, | |
| }); | |
| } | |
| }, | |
| }; | |
| `; | |
| // POST /api/proxy-pools/cloudflare-deploy | |
| export async function POST(request) { | |
| try { | |
| const body = await request.json(); | |
| const accountId = body.accountId?.trim(); | |
| const apiToken = body.apiToken?.trim(); | |
| const projectName = body.projectName?.trim() || `relay-${Date.now().toString(36)}`; | |
| if (!accountId || !apiToken) { | |
| return NextResponse.json({ error: "Cloudflare Account ID and API Token are required" }, { status: 400 }); | |
| } | |
| // 1. Upload Worker Script | |
| const workerScriptUrl = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/scripts/${projectName}`; | |
| // Cloudflare requires multipart/form-data for worker script upload | |
| const formData = new FormData(); | |
| formData.append("index.js", new Blob([RELAY_WORKER_CODE], { type: "application/javascript+module" }), "index.js"); | |
| formData.append("metadata", new Blob([JSON.stringify({ | |
| main_module: "index.js", | |
| compatibility_date: "2024-03-20", | |
| observability: { enabled: true } | |
| })], { type: "application/json" }), "metadata.json"); | |
| const uploadRes = await fetch(workerScriptUrl, { | |
| method: "PUT", | |
| headers: { | |
| Authorization: `Bearer ${apiToken}`, | |
| }, | |
| body: formData, | |
| }); | |
| if (!uploadRes.ok) { | |
| const err = await uploadRes.json().catch(() => ({})); | |
| console.error("Cloudflare upload error:", err); | |
| return NextResponse.json( | |
| { error: err.errors?.[0]?.message || "Failed to upload Worker to Cloudflare" }, | |
| { status: uploadRes.status } | |
| ); | |
| } | |
| // 2. Enable workers.dev subdomain for the script | |
| const enableSubdomainRes = await fetch(`${workerScriptUrl}/subdomain`, { | |
| method: "POST", | |
| headers: { | |
| Authorization: `Bearer ${apiToken}`, | |
| "Content-Type": "application/json", | |
| }, | |
| body: JSON.stringify({ enabled: true }), | |
| }); | |
| if (!enableSubdomainRes.ok) { | |
| const err = await enableSubdomainRes.json().catch(() => ({})); | |
| console.error("Cloudflare subdomain enable error:", err); | |
| // We don't fail completely here, just continue | |
| } | |
| // 3. Get the workers.dev subdomain for the account to construct the final URL | |
| let deployUrl = ""; | |
| const subdomainRes = await fetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/subdomain`, { | |
| method: "GET", | |
| headers: { | |
| Authorization: `Bearer ${apiToken}`, | |
| "Content-Type": "application/json", | |
| }, | |
| }); | |
| if (subdomainRes.ok) { | |
| const subdomainData = await subdomainRes.json(); | |
| if (subdomainData.result && subdomainData.result.subdomain) { | |
| deployUrl = `https://${projectName}.${subdomainData.result.subdomain}.workers.dev`; | |
| } | |
| } | |
| if (!deployUrl) { | |
| return NextResponse.json( | |
| { error: "Worker deployed but failed to retrieve workers.dev subdomain. Make sure you have setup a workers.dev subdomain in Cloudflare Dashboard." }, | |
| { status: 400 } | |
| ); | |
| } | |
| // Create proxy pool entry with type cloudflare | |
| const proxyPool = await createProxyPool({ | |
| name: projectName, | |
| proxyUrl: deployUrl, | |
| type: "cloudflare", | |
| noProxy: "", | |
| isActive: true, | |
| strictProxy: false, | |
| }); | |
| return NextResponse.json({ proxyPool, deployUrl }, { status: 201 }); | |
| } catch (error) { | |
| console.log("Error deploying Cloudflare relay:", error); | |
| return NextResponse.json({ error: error.message || "Deploy failed" }, { status: 500 }); | |
| } | |
| } | |