File size: 2,096 Bytes
6db5454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * Sibling route to `[...path]/route.ts` that handles the upstream root
 * (`GET /` on the backend — health/summary).
 *
 * Why: Next 15's catch-all segment `[...path]` does not match an empty
 * path, so `fetch("/api/proxy/")` 308-redirects to `/api/proxy` and 404s.
 * `lib/api.ts` calls `health()` as `request("")`, which lands here.
 */

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 forwardRoot(req: NextRequest) {
  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 upstreamUrl = `${API_URL}/${req.nextUrl.search}`;

  const upstreamHeaders: Record<string, string> = {
    Authorization: `Bearer ${TOKEN}`,
    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 text = await resp.text();
  return new NextResponse(text, {
    status: resp.status,
    headers: { "content-type": contentType },
  });
}

export const GET = forwardRoot;
export const POST = forwardRoot;
export const PUT = forwardRoot;
export const DELETE = forwardRoot;
export const PATCH = forwardRoot;