diff --git a/public/test-api.html b/public/test-api.html index 4188be1..323cefd 100644 --- a/public/test-api.html +++ b/public/test-api.html @@ -10,50 +10,39 @@ main { max-width: 960px; margin: 0 auto; } h1 { font-size: 1.5rem; color: #58a6ff; margin-bottom: 0.25rem; } .sub { color: #8b949e; margin-bottom: 1.5rem; font-size: 0.9rem; } - .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; margin-bottom: 1rem; overflow: hidden; } .card-header { padding: 0.75rem 1rem; background: #1c2128; border-bottom: 1px solid #30363d; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 0.5rem; user-select: none; } .card-header:hover { background: #21262d; } - .card-header .method { font-size: 0.7rem; padding: 1px 6px; border-radius: 3px; font-weight: 600; } + .card-header .method { font-size: 0.7rem; padding: 1px 6px; border-radius: 3px; font-weight: 600; text-transform: uppercase; } .get { background: #1f6feb33; color: #58a6ff; } .post { background: #23863633; color: #3fb950; } .card-body { padding: 1rem; display: none; } .card.open .card-body { display: block; } .card-header .arrow { margin-left: auto; transition: transform .15s; } .card.open .arrow { transform: rotate(90deg); } - label { display: block; font-size: 0.8rem; color: #8b949e; margin-bottom: 0.25rem; } input, textarea, select { width: 100%; background: #0d1117; border: 1px solid #30363d; border-radius: 4px; color: #e1e4e8; padding: 0.5rem 0.75rem; font-family: inherit; font-size: 0.9rem; margin-bottom: 0.75rem; } input:focus, textarea:focus { border-color: #58a6ff; outline: none; } textarea { min-height: 60px; resize: vertical; font-family: 'SF Mono', 'Cascadia Code', monospace; font-size: 0.8rem; } textarea.code { min-height: 120px; } - .row { display: flex; gap: 0.75rem; } .row > * { flex: 1; } - .btn { background: #238636; color: #fff; border: none; border-radius: 6px; padding: 0.5rem 1.25rem; font-size: 0.85rem; cursor: pointer; font-weight: 500; } .btn:hover { background: #2ea043; } .btn:disabled { opacity: 0.5; cursor: not-allowed; } - .btn-outline { background: transparent; border: 1px solid #30363d; color: #c9d1d9; } - .btn-outline:hover { background: #21262d; border-color: #8b949e; } - .btn-danger { background: #da3633; } - .btn-danger:hover { background: #f85149; } - .output { background: #0d1117; border: 1px solid #30363d; border-radius: 4px; padding: 0.75rem; margin-top: 0.5rem; } .output pre { font-family: 'SF Mono', 'Cascadia Code', monospace; font-size: 0.78rem; white-space: pre-wrap; word-break: break-all; max-height: 300px; overflow: auto; color: #c9d1d9; } .output .meta { font-size: 0.75rem; color: #8b949e; margin-bottom: 0.5rem; } .output .meta .status.ok { color: #3fb950; } .output .meta .status.err { color: #f85149; } - .toast { position: fixed; bottom: 1.5rem; right: 1.5rem; background: #1c2128; border: 1px solid #30363d; border-radius: 6px; padding: 0.75rem 1rem; font-size: 0.85rem; display: none; z-index: 100; }
-

Edge Proxy Relay — API Test

-

Test all proxy endpoints from the browser

+

Edge Proxy Relay — Interactive Test

+

Test all endpoints from the browser. Open a card, fill in the fields, and run.

-
AUTH API Key @@ -68,7 +57,6 @@
-
GET /health @@ -80,7 +68,6 @@
-
GET /v1/models @@ -92,15 +79,16 @@
-
POST /v1/chat/completions
- - +
+
+
+
@@ -109,26 +97,26 @@
-
POST /v1/messages (Anthropic)
- - - - +
+
+
+
+ + - +
-
RELAY Generic HTTP Relay @@ -137,10 +125,10 @@
- - - - +
+
+
+
@@ -247,17 +235,17 @@ async function callChat() { catch { showToast('Invalid messages JSON', false); return; } const body = { - model: document.getElementById('chatModel').value || 'gpt-4o-mini', + model: document.getElementById('chatModel').value || 'deepseek-v4-flash-free', messages, stream, - max_tokens: 128, + max_tokens: parseInt(document.getElementById('chatMaxTokens').value) || 128, }; try { if (stream) { const el = document.getElementById('output-chat'); el.style.display = 'block'; - el.innerHTML = `
streaming text/event-stream …
`;
+      el.innerHTML = `
streaming …
`;
       const resp = await fetch(apiBase() + '/v1/chat/completions', {
         method: 'POST',
         headers: authHeaders(),
@@ -292,11 +280,16 @@ async function callAnthropic() {
   catch { showToast('Invalid messages JSON', false); return; }
 
   const body = {
-    model: document.getElementById('anthModel').value || 'claude-sonnet-4-6',
+    model: document.getElementById('anthModel').value || 'deepseek-v4-flash-free',
     max_tokens: parseInt(document.getElementById('anthMaxTokens').value) || 256,
     messages,
     stream,
   };
+  // Parse optional system prompt with cache_control support
+  const sys = document.getElementById('anthSystem').value.trim();
+  if (sys) {
+    try { body.system = JSON.parse(sys); } catch { body.system = sys; }
+  }
 
   try {
     if (stream) {
diff --git a/src/index.test.ts b/src/index.test.ts
index 16b27ea..8ba3fc2 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -11,7 +11,6 @@ import { test, expect, describe, beforeAll, afterAll } from "bun:test";
 // Note: this will also start the Bun.serve() instance, which we allow.
 import {
 	handleHealth,
-	handleDocs,
 	handleIndex,
 	getClientIP,
 } from "./index";
@@ -37,27 +36,6 @@ describe("handleHealth", () => {
 	});
 });
 
-describe("handleDocs", () => {
-	test("should return 200 with HTML content", () => {
-		const response = handleDocs();
-		expect(response.status).toBe(200);
-		expect(response.headers.get("Content-Type")).toContain("text/html");
-	});
-
-	test("should include Edge Proxy Relay in the HTML", async () => {
-		const response = handleDocs();
-		const text = await response.text();
-		expect(text).toContain("Edge Proxy Relay");
-		expect(text).toContain("x-relay-target");
-		expect(text).toContain("WebSocket");
-	});
-
-	test("should include CORS header", () => {
-		const response = handleDocs();
-		expect(response.headers.get("Access-Control-Allow-Origin")).toBe("*");
-	});
-});
-
 describe("handleIndex", () => {
 	test("should return 200 with HTML content", () => {
 		const response = handleIndex();
diff --git a/src/index.ts b/src/index.ts
index 4858b40..35a2f47 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -134,124 +134,38 @@ function handleHealth(): Response {
 	);
 }
 
-/** Simple embedded HTML documentation page. */
-function handleDocs(): Response {
-	const html = `
-	
-	
-	  
-	  
-	  Edge Proxy Relay — Docs
-	  
-	
-	
-	
-

Edge Proxy Relay

-

Forward HTTP and WebSocket requests to any target server via the x-relay-target header.

- -

Endpoints

- -
-

GET /health

-

Health check. Returns 200 OK with server status, uptime, and version.

-
- -
-

GET /docs

-

This page.

-
- -
-

Any Path (Catch-all Relay)

-

Send a request with the x-relay-target header and this proxy forwards it.

-
- -

Usage — HTTP Relay

-
curl -s \\
-	  -H "x-relay-target: https://httpbin.org" \\
-	  -H "x-relay-path: /get" \\
-	  "https://your-proxy.example/any/path"
- - - - -
HeaderRequiredDescription
x-relay-targetYesBase URL of the upstream (http:// or https://)
x-relay-pathNoPath to append (default: /)
- -

Usage — WebSocket Relay

-
const ws = new WebSocket("wss://your-proxy.example/relay", {
-	  headers: { "x-relay-target": "wss://echo-websocket.example" },
-	});
-	ws.onopen = () => ws.send("Hello via relay!");
-	ws.onmessage = (e) => console.log("Got:", e.data);
- -

Status Codes

- - - - - - - - - -
CodeMeaning
204CORS preflight success (OPTIONS)
400Missing x-relay-target header
403Target blocked (SSRF protection / not allowed)
413Request body exceeds size limit
429Rate limit exceeded
502Upstream network / DNS error
504Upstream timeout
-
- - `; - - return new Response(html, { - status: 200, - headers: { - "Content-Type": "text/html; charset=utf-8", - ...getCorsHeaders(), - }, - }); -} - -/** Minimal status page shown at the root `/`. */ +/** Status page shown at the root `/`. */ function handleIndex(): Response { const html = ` - - - - - Edge Proxy Relay - - - -
-

Edge Proxy Relay

-

Server is running

-

/health · /docs

-
- - `; + + + + + Edge Proxy Relay + + + +
+

Edge Proxy Relay

+

Server is running

+ +
+ +`; return new Response(html, { status: 200, @@ -730,7 +644,6 @@ export type { WSRelayData }; export { server, handleHealth, - handleDocs, handleIndex, handleRelay, getClientIP, diff --git a/src/lib/router.ts b/src/lib/router.ts index 0a0e731..a5468ab 100644 --- a/src/lib/router.ts +++ b/src/lib/router.ts @@ -163,89 +163,244 @@ function handleIndex(): Response { }); } -function handleDocs(isWebSocketSupported: boolean): Response { +function handleDocs(_isWebSocketSupported: boolean): Response { const html = ` - - - - - Edge Proxy Relay — Docs - - - -
-

Edge Proxy Relay

-

Forward HTTP${isWebSocketSupported ? ' and WebSocket' : ''} requests to any target server via the x-relay-target header.

+ + + + + Edge Proxy — API Test + + + +
+

Edge Proxy Relay — Interactive Test

+

Test all endpoints from the browser. Open a card, fill in the fields, and run.

-

Endpoints

+
+
+ AUTH API Key + +
+
+ +
+ + +
+
+
-
-

GET /health

-

Health check. Returns 200 OK with server status, uptime, and version.

-
+
+
+ GET /health + +
+
+ + +
+
-
-

GET /docs

-

This page.

-
+
+
+ GET /v1/models + +
+
+ + +
+
-
-

Any Path (Catch-all Relay)

-

Send a request with the x-relay-target header and this proxy forwards it.

-
+
+
+ POST /v1/chat/completions + +
+
+
+
+
+
+ + + +
+ +
+
-

Usage — HTTP Relay

-
curl -s \\
-	  -H "x-relay-target: https://httpbin.org" \\
-	  -H "x-relay-path: /get" \\
-	  "https://your-proxy.example/any/path"
- - - - -
HeaderRequiredDescription
x-relay-targetYesBase URL of the upstream (http:// or https://)
x-relay-pathNoPath to append (default: /)
+
+
+ POST /v1/messages (Anthropic) + +
+
+
+
+
+
+ + + + + +
+ +
+
-${isWebSocketSupported ? ` -

Usage — WebSocket Relay

-
const ws = new WebSocket("wss://your-proxy.example/relay", {
-	  headers: { "x-relay-target": "wss://echo-websocket.example" },
-	});
-	ws.onopen = () => ws.send("Hello via relay!");
-	ws.onmessage = (e) => console.log("Got:", e.data);
-` : `

Note: WebSocket relay is not available on this deployment.

`} +
+
+ RELAY Generic HTTP Relay + +
+
+ + +
+
+
+
+ + +
+ +
+
-

Status Codes

- - - - - - - - - -
CodeMeaning
204CORS preflight success (OPTIONS)
400Missing x-relay-target header
403Target blocked (SSRF protection / not allowed)
413Request body exceeds size limit
429Rate limit exceeded
502Upstream network / DNS error
504Upstream timeout
-
- - `; +
+
+ + + +`; return new Response(html, { status: 200, diff --git a/src/worker.ts b/src/worker.ts index ec11d10..99bdd32 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -117,92 +117,6 @@ function handleHealth(): Response { ); } -function handleDocs(): Response { - const html = ` - - - - - Edge Proxy Relay — Docs - - - -
-

Edge Proxy Relay

-

Forward HTTP requests to any target server via the x-relay-target header.

- -

Endpoints

- -
-

GET /health

-

Health check. Returns 200 OK with server status, uptime, and version.

-
- -
-

GET /docs

-

This page.

-
- -
-

Any Path (Catch-all Relay)

-

Send a request with the x-relay-target header and this proxy forwards it.

-
- -

Usage — HTTP Relay

-
curl -s \\
-  -H "x-relay-target: https://httpbin.org" \\
-  -H "x-relay-path: /get" \\
-  "https://your-proxy.example/any/path"
- - - - -
HeaderRequiredDescription
x-relay-targetYesBase URL of the upstream (http:// or https://)
x-relay-pathNoPath to append (default: /)
- -

Status Codes

- - - - - - - - - -
CodeMeaning
204CORS preflight success (OPTIONS)
400Missing x-relay-target header
403Target blocked (SSRF protection / not allowed)
413Request body exceeds size limit
429Rate limit exceeded
502Upstream network / DNS error
504Upstream timeout
- -

Note: WebSocket relay is not available on this deployment.

-
- -`; - - return new Response(html, { - status: 200, - headers: { - "Content-Type": "text/html; charset=utf-8", - ...getCorsHeaders(), - }, - }); -} - function handleIndex(): Response { const html = ` @@ -215,16 +129,22 @@ function handleIndex(): Response { body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; color: #e1e4e8; background: #0d1117; display: flex; align-items: center; justify-content: center; min-height: 100vh; } main { text-align: center; } h1 { font-size: 2rem; color: #58a6ff; margin-bottom: 0.5rem; } - p { color: #8b949e; } + p { color: #8b949e; margin: 0.5rem 0; } a { color: #58a6ff; } .status { color: #3fb950; } + .links { margin-top: 1.5rem; display: flex; gap: 1rem; justify-content: center; } + .links a { text-decoration: none; background: #161b22; border: 1px solid #30363d; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.9rem; } + .links a:hover { background: #1c2128; border-color: #58a6ff; }

Edge Proxy Relay

Server is running

-

/health · /docs

+
`;