Files
dark-lord/proxy.ts
drjones 78a071ba02 Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation.

Made-with: Cursor
2026-04-07 21:35:52 -07:00

145 lines
3.5 KiB
TypeScript

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 <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/<segment>/` 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);
}