Sifa professional network frontend (Next.js, React, TailwindCSS) sifa.id/
at main 91 lines 2.2 kB view raw
1import { NextResponse, type NextRequest } from 'next/server'; 2 3interface SessionResponse { 4 authenticated: boolean; 5 did: string; 6 handle: string; 7 displayName: string; 8} 9 10function handleAssetProtection(request: NextRequest): NextResponse | null { 11 const referer = request.headers.get('referer'); 12 const host = request.headers.get('host') ?? ''; 13 14 if (!referer) { 15 return null; 16 } 17 18 const isOwnSite = referer.includes(host) || referer.includes('sifa.id'); 19 20 if (!isOwnSite) { 21 return new NextResponse(null, { status: 403 }); 22 } 23 24 return null; 25} 26 27async function handleAdminProtection(request: NextRequest): Promise<NextResponse | null> { 28 const sessionCookie = request.cookies.get('session'); 29 30 if (!sessionCookie?.value) { 31 return NextResponse.redirect(new URL('/login', request.url)); 32 } 33 34 const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3100'; 35 36 let session: SessionResponse; 37 try { 38 const response = await fetch(`${apiUrl}/api/auth/session`, { 39 headers: { 40 cookie: `session=${sessionCookie.value}`, 41 }, 42 }); 43 44 if (!response.ok) { 45 return NextResponse.redirect(new URL('/login', request.url)); 46 } 47 48 session = (await response.json()) as SessionResponse; 49 } catch { 50 return NextResponse.redirect(new URL('/login', request.url)); 51 } 52 53 if (!session.authenticated) { 54 return NextResponse.redirect(new URL('/login', request.url)); 55 } 56 57 const adminDids = (process.env.ADMIN_DIDS ?? '') 58 .split(',') 59 .map((did) => did.trim()) 60 .filter(Boolean); 61 62 if (!adminDids.includes(session.did)) { 63 return NextResponse.redirect(new URL('/', request.url)); 64 } 65 66 return null; 67} 68 69export async function middleware(request: NextRequest): Promise<NextResponse> { 70 const { pathname } = request.nextUrl; 71 72 if (pathname.startsWith('/admin')) { 73 const adminResponse = await handleAdminProtection(request); 74 if (adminResponse) { 75 return adminResponse; 76 } 77 return NextResponse.next(); 78 } 79 80 // Asset hotlink protection 81 const assetResponse = handleAssetProtection(request); 82 if (assetResponse) { 83 return assetResponse; 84 } 85 86 return NextResponse.next(); 87} 88 89export const config = { 90 matcher: ['/admin/:path*', '/assets/:path*'], 91};