| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { NextRequest, NextResponse } from "next/server"; |
|
|
| const API_URL = process.env.D2L_API_URL || "https://etiya-d2l-api.hf.space"; |
| const TOKEN = process.env.HF_TOKEN; |
|
|
| export const dynamic = "force-dynamic"; |
| export const runtime = "nodejs"; |
|
|
| async function handle(req: NextRequest, params: { path: string[] }) { |
| if (!TOKEN) { |
| return NextResponse.json( |
| { |
| error: |
| "HF_TOKEN env var not set on server. Copy .env.example to .env.local and add your token.", |
| }, |
| { status: 500 } |
| ); |
| } |
|
|
| const path = (params.path ?? []).join("/"); |
| const search = req.nextUrl.search; |
| const upstreamUrl = `${API_URL}/${path}${search}`; |
|
|
| const upstreamHeaders: Record<string, string> = { |
| Authorization: `Bearer ${TOKEN}`, |
| |
| Accept: req.headers.get("accept") || "application/json", |
| }; |
|
|
| let body: BodyInit | null = null; |
| if (req.method !== "GET" && req.method !== "HEAD") { |
| const contentType = req.headers.get("content-type") || "application/json"; |
| upstreamHeaders["Content-Type"] = contentType; |
| body = await req.text(); |
| } |
|
|
| let resp: Response; |
| try { |
| resp = await fetch(upstreamUrl, { |
| method: req.method, |
| headers: upstreamHeaders, |
| body, |
| cache: "no-store", |
| }); |
| } catch (e: unknown) { |
| return NextResponse.json( |
| { |
| error: "upstream_unreachable", |
| message: e instanceof Error ? e.message : String(e), |
| upstream: upstreamUrl, |
| }, |
| { status: 502 } |
| ); |
| } |
|
|
| |
| |
| |
| const contentType = resp.headers.get("content-type") || "application/json"; |
| const headers: Record<string, string> = { |
| "content-type": contentType, |
| "cache-control": resp.headers.get("cache-control") ?? "no-cache, no-transform", |
| }; |
| if (contentType.includes("text/event-stream")) { |
| headers["x-accel-buffering"] = "no"; |
| headers["connection"] = "keep-alive"; |
| } |
| return new NextResponse(resp.body, { |
| status: resp.status, |
| headers, |
| }); |
| } |
|
|
| |
| type RouteCtx = { params: Promise<{ path: string[] }> }; |
|
|
| export async function GET(req: NextRequest, ctx: RouteCtx) { |
| return handle(req, await ctx.params); |
| } |
| export async function POST(req: NextRequest, ctx: RouteCtx) { |
| return handle(req, await ctx.params); |
| } |
| export async function PUT(req: NextRequest, ctx: RouteCtx) { |
| return handle(req, await ctx.params); |
| } |
| export async function DELETE(req: NextRequest, ctx: RouteCtx) { |
| return handle(req, await ctx.params); |
| } |
| export async function PATCH(req: NextRequest, ctx: RouteCtx) { |
| return handle(req, await ctx.params); |
| } |
|
|