Files
robertseares/frontend/middleware.js
a2a-platform 34675d168a deploy
2026-06-11 23:28:20 +00:00

125 lines
3.0 KiB
JavaScript

import { NextResponse } from "next/server";
const LOCALE_COOKIE = "rs_locale";
const SUPPORTED_LOCALES = new Set(["en", "pt"]);
const PUBLIC_FILE = /\.(?:ico|png|jpg|jpeg|svg|webp|avif|gif|txt|xml|json|webmanifest|css|js|map|woff2?)$/i;
const RESERVED_PREFIXES = [
"/_next",
"/_a2a",
"/.well-known",
"/api",
"/auth",
"/invoke",
"/mcp",
"/message:send",
"/message:stream",
"/tasks",
"/answers",
"/input-requests",
"/scope-grants",
"/scope-denials",
];
function isReservedPath(pathname) {
if (PUBLIC_FILE.test(pathname)) {
return true;
}
return RESERVED_PREFIXES.some(
(prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),
);
}
function normalizeLocale(value) {
const locale = String(value || "").trim().toLowerCase();
if (SUPPORTED_LOCALES.has(locale)) {
return locale;
}
return null;
}
function localeFromCountry(request) {
const country = (
request.headers.get("x-vercel-ip-country")
|| request.headers.get("cf-ipcountry")
|| request.headers.get("x-country-code")
|| ""
).trim().toUpperCase();
if (country === "BR" || country === "PT") {
return "pt";
}
return null;
}
function localeFromAcceptLanguage(request) {
const header = request.headers.get("accept-language") || "";
const ranked = header
.split(",")
.map((part) => {
const [tag, ...params] = part.trim().split(";");
const q = params
.map((param) => param.trim())
.find((param) => param.startsWith("q="));
const weight = q ? Number.parseFloat(q.slice(2)) : 1;
return {
language: tag.toLowerCase().split("-")[0],
weight: Number.isFinite(weight) ? weight : 1,
};
})
.filter((item) => SUPPORTED_LOCALES.has(item.language))
.sort((a, b) => b.weight - a.weight);
return ranked[0]?.language || null;
}
function preferredLocale(request) {
return (
normalizeLocale(request.cookies.get(LOCALE_COOKIE)?.value)
|| localeFromAcceptLanguage(request)
|| localeFromCountry(request)
|| "en"
);
}
function withLocaleCookie(response, locale) {
response.cookies.set(LOCALE_COOKIE, locale, {
path: "/",
maxAge: 60 * 60 * 24 * 365,
sameSite: "lax",
});
return response;
}
export function middleware(request) {
const url = request.nextUrl;
const { pathname, searchParams } = url;
if (isReservedPath(pathname)) {
return NextResponse.next();
}
const requestedLocale = normalizeLocale(searchParams.get("lang"));
if (requestedLocale) {
const cleanUrl = url.clone();
cleanUrl.searchParams.delete("lang");
const response =
cleanUrl.toString() === url.toString()
? NextResponse.next()
: NextResponse.redirect(cleanUrl);
return withLocaleCookie(response, requestedLocale);
}
if (pathname === "/") {
const locale = preferredLocale(request);
if (locale === "pt") {
const redirectUrl = url.clone();
redirectUrl.pathname = "/pt/";
return NextResponse.redirect(redirectUrl);
}
}
return NextResponse.next();
}
export const config = {
matcher: ["/:path*"],
};