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 }); } }