import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { CYBERLUX_ENTRY_COOKIE, parseCyberluxEntry, type CyberluxEntry, } from "@/lib/cyberluxEntry"; import { DEDICATED_ROOT } from "@/lib/onionRoutes.generated"; /** Set by nginx per Tor vhost: `proxy_set_header X-Cyberlux-Node ;` */ function entryFromRequest(request: NextRequest): CyberluxEntry { return parseCyberluxEntry(request.headers.get("x-cyberlux-node")); } /** * Paths that must not be prefixed when using a dedicated entry onion. * Keep in sync with top-level `app//` routes. */ const CROSS_NAV_PREFIXES = [ "/account", "/api", "/arb-academy", "/awards", "/barter", "/chatter", "/checkout", "/comparison", "/conspiracies", "/darknet-atlas", "/dashboard", "/drop-box", "/drops", "/easter-eggs", "/exchange", "/forum", "/game", "/hidden-wiki", "/inner-circle", "/links", "/market", "/messages", "/mixer", "/presswire", "/raffle", "/red-room", "/reviews", "/sanctuary", "/search", "/secret-layer", "/security-analysis", "/sign-in", "/sign-up", "/support", "/syndicate", "/testimonials", "/trees", "/trust", "/vault", "/vendor", "/vendors", "/wallets", "/webring", "/w/", ] as const; function isCrossNavPath(pathname: string): boolean { return CROSS_NAV_PREFIXES.some((p) => pathname.startsWith(p)); } function withEntryCookie(res: NextResponse, request: NextRequest): NextResponse { const entry = entryFromRequest(request); res.cookies.set(CYBERLUX_ENTRY_COOKIE, entry, { path: "/", sameSite: "lax", maxAge: 60 * 60 * 24 * 400, httpOnly: false, }); return res; } function handleDedicatedRoot( request: NextRequest, pathname: string, root: string, ): NextResponse { if (isCrossNavPath(pathname)) { return withEntryCookie(NextResponse.next(), request); } const suffix = pathname === "/" ? "" : pathname; return withEntryCookie( NextResponse.rewrite(new URL(`${root}${suffix}`, request.url)), request, ); } /** * Next.js 16+ proxy (replaces `middleware.ts`). Runs on Node; no `export const config` here. */ export function proxy(request: NextRequest) { const { pathname } = request.nextUrl; const entry = entryFromRequest(request); if (pathname.startsWith("/_next") || pathname.startsWith("/favicon")) { return withEntryCookie(NextResponse.next(), request); } // Same idea as legacy middleware `matcher`: skip typical static files if (/\.[a-zA-Z0-9]{2,5}$/.test(pathname)) { return withEntryCookie(NextResponse.next(), request); } if (entry === "wiki") { if (isCrossNavPath(pathname)) { return withEntryCookie(NextResponse.next(), request); } return withEntryCookie( NextResponse.rewrite(new URL("/hidden-wiki", request.url)), request, ); } if (entry === "w") { if (isCrossNavPath(pathname)) { return withEntryCookie(NextResponse.next(), request); } if (pathname === "/") { return withEntryCookie( NextResponse.rewrite(new URL("/syndicate", request.url)), request, ); } const rest = pathname.startsWith("/") ? pathname.slice(1) : pathname; return withEntryCookie( NextResponse.rewrite(new URL(`/w/${rest}`, request.url)), request, ); } if (entry !== "hub" && entry in DEDICATED_ROOT) { const root = DEDICATED_ROOT[entry as keyof typeof DEDICATED_ROOT]; return handleDedicatedRoot(request, pathname, root); } return withEntryCookie(NextResponse.next(), request); }