import { withAuth } from "next-auth/middleware" import { NextResponse } from "next/server" import { UserRole } from "@/types/database" export default withAuth( function middleware(req) { const token = req.nextauth.token const { pathname } = req.nextUrl // Allow token-based bypass for admin migrate endpoint (non-interactive deployments) const migrateToken = process.env.MIGRATE_TOKEN const headerToken = req.headers.get("x-migrate-token") const urlToken = req.nextUrl.searchParams.get("token") const hasMigrateBypass = pathname.startsWith("/api/admin/migrate") && ((headerToken && headerToken === migrateToken) || (urlToken && urlToken === migrateToken)) // Admin routes protection if (pathname.startsWith("/admin")) { if (!token) { return NextResponse.redirect(new URL("/auth/signin", req.url)) } // Check if user has admin role const userRole = token.role as UserRole if (userRole !== UserRole.SHOP_ADMIN && userRole !== UserRole.SUPER_ADMIN) { return NextResponse.redirect(new URL("/unauthorized", req.url)) } } // Artist dashboard routes if (pathname.startsWith("/artist-dashboard")) { if (!token) { return NextResponse.redirect(new URL("/auth/signin", req.url)) } const userRole = token.role as UserRole if (userRole !== UserRole.ARTIST && userRole !== UserRole.SHOP_ADMIN && userRole !== UserRole.SUPER_ADMIN) { return NextResponse.redirect(new URL("/unauthorized", req.url)) } } // Legacy artist-specific routes (if any) if (pathname.startsWith("/artist") && !pathname.startsWith("/artists")) { if (!token) { return NextResponse.redirect(new URL("/auth/signin", req.url)) } const userRole = token.role as UserRole if (userRole !== UserRole.ARTIST && userRole !== UserRole.SHOP_ADMIN && userRole !== UserRole.SUPER_ADMIN) { return NextResponse.redirect(new URL("/unauthorized", req.url)) } } // API routes protection if (pathname.startsWith("/api/admin")) { // Bypass for migration endpoint with valid token (used for automated deploys) if (hasMigrateBypass) { return NextResponse.next() } if (!token) { return NextResponse.json({ error: "Authentication required" }, { status: 401 }) } const userRole = token.role as UserRole if (userRole !== UserRole.SHOP_ADMIN && userRole !== UserRole.SUPER_ADMIN) { return NextResponse.json({ error: "Insufficient permissions" }, { status: 403 }) } } return NextResponse.next() }, { callbacks: { authorized: ({ token, req }) => { const { pathname } = req.nextUrl // Token-based bypass for migration endpoint (before auth checks) const migrateToken = process.env.MIGRATE_TOKEN const headerToken = req.headers.get("x-migrate-token") const urlToken = req.nextUrl.searchParams.get("token") if ( pathname.startsWith("/api/admin/migrate") && ((headerToken && headerToken === migrateToken) || (urlToken && urlToken === migrateToken)) ) { return true } // Public routes that don't require authentication const publicRoutes = [ "/", "/artists", "/contact", "/book", "/aftercare", "/gift-cards", "/specials", "/terms", "/privacy", "/auth/signin", "/auth/error", "/unauthorized" ] // Allow public routes and artist portfolio pages if (publicRoutes.some(route => pathname === route || pathname.startsWith(route))) { return true } // Allow individual artist portfolio pages (public access) if (pathname.match(/^\/artists\/[^\/]+$/)) { return true } // Allow public API routes if (pathname.startsWith("/api/auth") || pathname.startsWith("/api/public")) { return true } // Require authentication for all other routes return !!token }, }, } ) export const config = { matcher: [ /* * Match all request paths except for the ones starting with: * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) * - public folder */ "/((?!_next/static|_next/image|favicon.ico|public|.*\\.png$|.*\\.jpg$|.*\\.jpeg$|.*\\.gif$|.*\\.svg$).*)", ], }