SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
1.8 KB · 42 lines typescript
Raw Blame History
1import { NextResponse, type NextRequest } from 'next/server';23/**4 * Request guard for the admin surface (§144). Runs before rendering: `/admin/*` and `/api/admin/*`5 * require the `ri_admin` cookie whose value is HMAC-SHA256(SESSION_SECRET, "admin:" + ADMIN_TOKEN).6 * Web Crypto is used because this runs on the edge-compatible runtime.7 */8const ADMIN_COOKIE = 'ri_admin';910async function expectedCookie(): Promise<string | null> {11  const token = process.env.ADMIN_TOKEN;12  if (!token) return null;13  const enc = new TextEncoder();14  const key = await crypto.subtle.importKey('raw', enc.encode(process.env.SESSION_SECRET ?? 'dev-only'), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);15  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(`admin:${token}`));16  return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, '0')).join('');17}1819export async function proxy(req: NextRequest) {20  const { pathname } = req.nextUrl;21  if (pathname === '/admin/login' || pathname === '/api/admin/login') return NextResponse.next();22  const expected = await expectedCookie();23  const got = req.cookies.get(ADMIN_COOKIE)?.value;24  const ok = Boolean(expected && got && got.length === expected.length && constantTimeEqual(got, expected));25  if (ok) return NextResponse.next();26  if (pathname.startsWith('/api/')) return NextResponse.json({ error: 'Admin authentication required' }, { status: 401 });27  const url = req.nextUrl.clone();28  url.pathname = '/admin/login';29  url.searchParams.set('next', pathname);30  return NextResponse.redirect(url);31}3233function constantTimeEqual(a: string, b: string): boolean {34  let out = 0;35  for (let i = 0; i < a.length; i++) out |= a.charCodeAt(i) ^ b.charCodeAt(i);36  return out === 0;37}3839export const config = {40  matcher: ['/admin/:path*', '/api/admin/:path*'],41};42