From f893228ee590f5f5a1b48215a0891ce10bf195ec Mon Sep 17 00:00:00 2001 From: skidoodle Date: Sun, 21 Dec 2025 01:46:34 +0100 Subject: [PATCH] feat: Add proxy and validation API routes with `undici` and an insecure HTTP agent. --- bun.lock | 37 ++++++++++++++++------------- package.json | 3 ++- src/app/proxy/[...slug]/route.ts | 6 ++++- src/app/validate/[...slug]/route.ts | 14 +++++++---- src/utils/http-agent.ts | 7 ++++++ 5 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 src/utils/http-agent.ts diff --git a/bun.lock b/bun.lock index 899f09f..23843e8 100644 --- a/bun.lock +++ b/bun.lock @@ -5,25 +5,26 @@ "": { "name": "erettsegi-browser", "dependencies": { - "@heroui/react": "latest", - "framer-motion": "latest", - "next": "latest", - "next-themes": "latest", - "react": "latest", - "react-dom": "latest", - "react-icons": "latest", + "@heroui/react": "^2.8.7", + "framer-motion": "^12.23.26", + "next": "16.1.0", + "next-themes": "^0.4.6", + "react": "19.2.3", + "react-dom": "19.2.3", + "react-icons": "^5.5.0", + "undici": "^7.16.0", }, "devDependencies": { - "@biomejs/biome": "latest", - "@tailwindcss/postcss": "latest", - "@types/node": "latest", - "@types/react": "latest", - "@types/react-dom": "latest", - "postcss": "latest", - "prettier": "latest", - "prettier-plugin-tailwindcss": "latest", - "tailwindcss": "latest", - "typescript": "latest", + "@biomejs/biome": "2.3.10", + "@tailwindcss/postcss": "^4.1.18", + "@types/node": "25.0.3", + "@types/react": "19.2.7", + "@types/react-dom": "19.2.3", + "postcss": "8.5.6", + "prettier": "^3.7.4", + "prettier-plugin-tailwindcss": "^0.7.2", + "tailwindcss": "^4.1.18", + "typescript": "5.9.3", }, }, }, @@ -636,6 +637,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "use-composed-ref": ["use-composed-ref@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w=="], diff --git a/package.json b/package.json index 18906c3..f407c26 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "next-themes": "^0.4.6", "react": "19.2.3", "react-dom": "19.2.3", - "react-icons": "^5.5.0" + "react-icons": "^5.5.0", + "undici": "^7.16.0" }, "devDependencies": { "@biomejs/biome": "2.3.10", diff --git a/src/app/proxy/[...slug]/route.ts b/src/app/proxy/[...slug]/route.ts index dcad16e..16c8241 100644 --- a/src/app/proxy/[...slug]/route.ts +++ b/src/app/proxy/[...slug]/route.ts @@ -1,5 +1,6 @@ import { type NextRequest, NextResponse } from "next/server"; import { parseSegments, getExternalUrl, type ResourceSlug } from "@/utils/edu"; +import { insecureAgent } from "@/utils/http-agent"; export async function GET( _req: NextRequest, @@ -32,7 +33,10 @@ export async function GET( ); } - const externalResponse = await fetch(targetUrl, { method: "GET" }); + const externalResponse = await fetch(targetUrl, { + method: "GET", + dispatcher: insecureAgent, + } as RequestInit); if (!externalResponse.ok) { return NextResponse.json( diff --git a/src/app/validate/[...slug]/route.ts b/src/app/validate/[...slug]/route.ts index 6cb775d..eb6b23c 100644 --- a/src/app/validate/[...slug]/route.ts +++ b/src/app/validate/[...slug]/route.ts @@ -1,5 +1,6 @@ import { type NextRequest, NextResponse } from "next/server"; import { parseSegments, getExternalUrl, type ResourceSlug } from "@/utils/edu"; +import { insecureAgent } from "@/utils/http-agent"; export async function GET( _req: NextRequest, @@ -10,7 +11,7 @@ export async function GET( const { subject, year, period, level, resourceType } = parseSegments(slug); if (!subject || !year || !period || !level || !resourceType) { - return NextResponse.json({ status: 400 }); + return NextResponse.json({ status: 400 }, { status: 400 }); } const targetUrl = getExternalUrl( @@ -20,11 +21,14 @@ export async function GET( level, resourceType as ResourceSlug, ); - if (!targetUrl) return NextResponse.json({ status: 404 }); + if (!targetUrl) return NextResponse.json({ status: 404 }, { status: 404 }); - const res = await fetch(targetUrl, { method: "HEAD" }); - return NextResponse.json({ status: res.status }); + const res = await fetch(targetUrl, { + method: "HEAD", + dispatcher: insecureAgent, + } as RequestInit); + return NextResponse.json({ status: res.status }, { status: res.status }); } catch { - return NextResponse.json({ status: 500 }); + return NextResponse.json({ status: 500 }, { status: 500 }); } } diff --git a/src/utils/http-agent.ts b/src/utils/http-agent.ts new file mode 100644 index 0000000..02ac961 --- /dev/null +++ b/src/utils/http-agent.ts @@ -0,0 +1,7 @@ +import { Agent } from "undici"; + +export const insecureAgent = new Agent({ + connect: { + rejectUnauthorized: false, + }, +});