diff --git a/src/middleware.ts b/src/middleware.ts index 0572da3..13421b9 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -3,6 +3,19 @@ import { NextResponse, type NextRequest } from "next/server"; const ENABLE_DRAFT_MODE_ROUTE = "/api/preview"; +/** + * Shape of a single entry returned by the Website Builder's `GET /wb/redirects` endpoint. + * Declared locally on purpose: middleware runs on every request, so it stays free of runtime + * dependencies, and `@webiny/website-builder-sdk` (where this type lives) is not a direct + * dependency of this project. + */ +interface PublicRedirect { + id: string; + from: string; + to: string; + permanent: boolean; +} + export async function middleware(request: NextRequest) { const { searchParams, pathname } = request.nextUrl; // Check if the preview/editing flag is set. @@ -57,23 +70,46 @@ export async function middleware(request: NextRequest) { } // Check if there's a redirect defined for the requested page. - const redirectsUrl = new URL( - `/api/redirects?wb.tenant=${tenantId}&pathname=${encodeURIComponent(pathname)}`, - request.url, - ); - + // + // This queries the Website Builder API directly rather than fetching our own /api/redirects + // route. Fetching our own origin from middleware costs a second function invocation and a full + // network round trip on every request, and it breaks in ways that are hard to see: + // + // - Behind a local HTTPS proxy the certificate isn't trusted by the Edge runtime, so the + // request throws (SELF_SIGNED_CERT_IN_CHAIN) and every redirect silently stops working. + // - On a deployment protected by Vercel Authentication, the self-request carries no + // credentials and is answered with a 401 or an auth redirect instead of our route. + // + // Talking to the API directly avoids both: it is the only host we need to reach, and it has a + // real certificate and its own authentication. try { - const redirectResponse = await fetch(redirectsUrl); + const response = await fetch( + `${process.env.NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST}/wb/redirects`, + { + headers: { + "X-Tenant": tenantId, + Authorization: `Bearer ${process.env.NEXT_PUBLIC_WEBSITE_BUILDER_API_KEY}`, + }, + }, + ); + + if (!response.ok) { + throw new Error(`Redirects lookup responded with ${response.status}.`); + } + + const redirects: PublicRedirect[] = await response.json(); + const redirect = redirects.find((item) => item.from === pathname); - const { redirect } = await redirectResponse.json(); if (redirect) { return NextResponse.redirect( new URL(redirect.to, request.url), redirect.permanent ? 308 : 307, ); } - } catch { - // Do nothing. Most probably redirect was simply not found. + } catch (err) { + // A failed lookup must not take the page down, but it must not be silent either: swallowing it + // is indistinguishable from "no redirect is configured" and hides real API failures. + console.error(`[middleware] Redirect lookup failed for "${pathname}":`, err); } // For all other requests, continue as normal without any modifications.