From a6333750e4f0a7e37d876cdd0841d45d9d96bbe7 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Thu, 7 May 2026 20:26:51 +0700 Subject: [PATCH] feat: update project structure and enhance UI for Edge Proxy Test - Add .gitignore to exclude api directory - Remove deprecated api/index.ts file - Create new public/index.html with improved UI and functionality - Update package.json build script to copy index.html to dist - Enhance request handling in index.html with custom headers and presets - Improve response display with status, time, and size metrics --- .gitignore | 1 + api/index.ts | 127 ----------- index.html | 557 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 2 +- public/index.html | 542 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1062 insertions(+), 167 deletions(-) delete mode 100644 api/index.ts create mode 100644 public/index.html diff --git a/.gitignore b/.gitignore index ecb0735..1942c0d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules out dist *.tgz +api # code coverage coverage diff --git a/api/index.ts b/api/index.ts deleted file mode 100644 index 39b83b4..0000000 --- a/api/index.ts +++ /dev/null @@ -1,127 +0,0 @@ -export const config = { runtime: "edge" }; - -// Allowlist: only these headers are forwarded to prevent leaking -// Vercel internal metadata, credentials, and infrastructure info -const ALLOWED_HEADERS = new Set([ - "content-type", - "accept", - "accept-encoding", - "accept-language", - "user-agent", - "referer", - "origin", - "authorization", - "proxy-authorization", - "cache-control", -]); - -// Blocklist: sensitive headers that should NEVER be forwarded -const BLOCKED_HEADERS = new Set([ - "x-vercel-id", - "x-vercel-deployment-url", - "x-vercel-oidc-token", - "x-vercel-oidc-token-ts", - "x-vercel-signature", - "x-vercel-edgified", - "x-vercel-ip-city", - "x-vercel-ip-country", - "x-vercel-ip-country-region", - "x-vercel-ip-latency", - "x-vercel-deployment-config", - "x-vercel-rewritten-query", - "cf-ray", - "cf-connecting-ip", - "cf-ipcountry", - "cf-ray-id", - "x-forwarded-for", - "x-forwarded-host", - "x-forwarded-proto", - "forwarded", - "cookie", - "set-cookie", - "x-real-ip", - "x-cluster-client-ip", - "x-api-key", - "x-cache", -]); - -const INTERNAL_HEADERS = new Set(["x-relay-target", "x-relay-path", "host"]); - -export function isAllowedTarget(url: string): boolean { - try { - const parsed = new URL(url); - return ["http:", "https:"].includes(parsed.protocol); - } catch { - return false; - } -} - -export function normalizeTargetUrl(target: string | null, relayPath: string): string | null { - if (!target) return null; - return target.replace(/\/$/, "") + relayPath; -} - -export function filterHeaders(headers: Headers): Headers { - const filtered = new Headers(); - for (const [key, value] of headers.entries()) { - const lowerKey = key.toLowerCase(); - if (INTERNAL_HEADERS.has(lowerKey)) continue; - if (BLOCKED_HEADERS.has(lowerKey)) continue; - if (lowerKey.startsWith("x-vercel-")) continue; - if (lowerKey.startsWith("cf-")) continue; - if (lowerKey.startsWith("x-forwarded-")) continue; - if (ALLOWED_HEADERS.has(lowerKey)) { - filtered.set(key, value); - continue; - } - filtered.set(key, value); - } - return filtered; -} - -export function shouldSendBody(method: string): boolean { - return method !== "GET" && method !== "HEAD"; -} - -const ALLOWED_METHODS = new Set(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]); - -export default async function handler(req: Request): Promise { - if (!ALLOWED_METHODS.has(req.method)) { - return new Response(JSON.stringify({ error: "Method not allowed" }), { - status: 405, - headers: { "content-type": "application/json" }, - }); - } - - const target = req.headers.get("x-relay-target"); - const relayPath = req.headers.get("x-relay-path") || "/"; - const targetUrl = normalizeTargetUrl(target, relayPath); - - if (!targetUrl) { - return new Response( - JSON.stringify({ error: "Missing x-relay-target header" }), - { status: 400, headers: { "content-type": "application/json" } }, - ); - } - - if (!isAllowedTarget(targetUrl)) { - return new Response( - JSON.stringify({ error: "Target domain not allowed" }), - { status: 403, headers: { "content-type": "application/json" } }, - ); - } - - const headers = filterHeaders(new Headers(req.headers)); - const fetchOptions: RequestInit = { - method: req.method, - headers, - body: shouldSendBody(req.method) ? req.body : undefined, - duplex: "half", - }; - - const response = await fetch(targetUrl, fetchOptions); - return new Response(response.body, { - status: response.status, - headers: response.headers, - }); -} \ No newline at end of file diff --git a/index.html b/index.html index 3c764a8..29dc9b5 100644 --- a/index.html +++ b/index.html @@ -3,64 +3,543 @@ - Edge Proxy Relay + Edge Proxy Test -

Edge Proxy Relay

-

Forward requests to external APIs via Vercel Edge

+

Edge Proxy Relay Test

+

Test your proxy with various endpoints and configurations

-
- - +
+

Target Configuration

+ +
+
+ + +
+
+ +
+ Quick URLs: + + + + + + +
-

Response:

-
Waiting for request...
+
+

Request Method

+
+ + + + + + + +
+
+ +
+

Request Body (JSON)

+
+
+ +
+
+ +
+ + + + +
+ + +
+
+
+ +
+

Custom Headers

+
+
+ + +
+
+ + +
+
+
+ + + +
+

Response

+
+
+ Status: + - +
+
+ Time: + - +
+
+ Size: + - +
+
+
+
+ +
Response will appear here...
+
+
+ +
Response headers will appear here...
+
+
+
\ No newline at end of file diff --git a/package.json b/package.json index 2353cb4..775e100 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "type": "module", "private": true, "scripts": { - "build": "bun build src/index.ts --target=bun --outdir=api", + "build": "bun build src/index.ts --target=bun --outdir=api && mkdir -p dist && cp public/index.html dist/index.html", "start": "bun run src/index.ts", "dev": "bun --hot run src/index.ts", "test": "bun test", diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..5fdf721 --- /dev/null +++ b/public/index.html @@ -0,0 +1,542 @@ + + + + + + Edge Proxy Test + + + +

Edge Proxy Relay Test

+

Test your proxy with various endpoints and configurations

+ +
+

Target Configuration

+ +
+
+ + +
+
+ +
+ Quick URLs: + + + + + + +
+
+ +
+

Request Method

+
+ + + + + + + +
+
+ +
+

Request Body (JSON)

+
+
+ +
+
+ +
+ + + + +
+ + +
+
+
+ +
+

Custom Headers

+
+
+ + +
+
+ + +
+
+
+ + + +
+

Response

+
+
+ Status: + - +
+
+ Time: + - +
+
+ Size: + - +
+
+
+
+ +
Response will appear here...
+
+
+ +
Response headers will appear here...
+
+
+
+ + + + \ No newline at end of file