SPB Git

Repository Public MIT

proxy.ts 2.1 KB 53 lines typescript

1import { NextResponse, type NextRequest } from "next/server";23/**4 * Per-request concerns that must run before rendering:5 *  - a nonce-based Content-Security-Policy (no 'unsafe-inline' scripts: package READMEs are untrusted);6 *  - pretty artifact URLs: /@namespace/slug → /a/namespace/slug;7 *  - /mcp is the MCP protocol endpoint (POST); a browser visiting it gets the MCP server listing.8 */9export function proxy(req: NextRequest) {10  const { pathname } = req.nextUrl;11  const nonce = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(16))));12  const dev = process.env.NODE_ENV !== "production";13  const csp = [14    "default-src 'self'",15    `script-src 'self' 'nonce-${nonce}' 'strict-dynamic'${dev ? " 'unsafe-eval'" : ""}`,16    "style-src 'self' 'unsafe-inline'",17    "img-src 'self' data:",18    "font-src 'self' data:",19    `connect-src 'self'${dev ? " ws:" : ""}`,20    "object-src 'none'",21    "base-uri 'self'",22    "form-action 'self'",23    "frame-ancestors 'none'",24  ].join("; ");2526  const headers = new Headers(req.headers);27  headers.set("x-nonce", nonce);28  headers.set("content-security-policy", csp);2930  let res: NextResponse;31  if (pathname.startsWith("/dashboard") && !req.cookies.has("skills_session")) {32    // Cheap early exit; the real check (session lookup) happens in every dashboard page and action.33    return NextResponse.redirect(new URL("/login", req.url));34  }35  if (pathname.startsWith("/@")) {36    const url = req.nextUrl.clone();37    url.pathname = "/a/" + pathname.slice(2);38    res = NextResponse.rewrite(url, { request: { headers } });39  } else if (pathname === "/mcp" && req.method === "GET" && (req.headers.get("accept") ?? "").includes("text/html")) {40    const url = req.nextUrl.clone();41    url.pathname = "/mcp-servers";42    res = NextResponse.rewrite(url, { request: { headers } });43  } else {44    res = NextResponse.next({ request: { headers } });45  }46  res.headers.set("Content-Security-Policy", csp);47  return res;48}4950export const config = {51  matcher: [{ source: "/((?!api/|_next/static|_next/image|cli/|favicon.ico|icon.svg|robots.txt|sitemap.xml).*)" }],52};53
Files main
Menu

Theme