united-tattoo/middleware.ts
Nicholai 1378bff909 updated the following components to use the API instead of hardcoded data:
### 1. __artists-grid.tsx__ (Main Artist Browsing)

- Uses  hook from
- Fetches from  endpoint
- Includes loading states, error handling, and filtering
- __Impact:__ Primary artist browsing experience now fully API-driven

### 2. __artist-portfolio.tsx__ (Individual Artist Pages)

- Uses  hook
- Fetches from  endpoint
- Fixed all TypeScript errors (changed image ID from number to string)
- Added loading/error states
- __Impact:__ Artist detail pages now fully API-driven

### 3. __booking-form.tsx__ (Artist Selection Dropdown)

- Uses  hook for artist selection
- Updated to use API data structure ( array, , etc.)
- Added loading state for dropdown
- __Impact:__ Booking flow now uses real artist data

## ⚠️ REMAINING (Decorative/Marketing Components)

Two complex components still use hardcoded :

### 4. __artists-section.tsx__ (Homepage Hero - 348 lines)

- Homepage marketing section with complex parallax scrolling
- Uses hardcoded artist data for visual cards
- __Non-blocking:__ This is a decorative homepage element

### 5. __artists-page-section.tsx__ (Artists Page Section - 413 lines)

- Full-page artists showcase with parallax effects
- Uses hardcoded artist data for visual layout
- __Non-blocking:__ Alternative to artists-grid.tsx (which IS using API)

##
2025-10-06 04:44:08 -06:00

116 lines
3.4 KiB
TypeScript

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
// 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")) {
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
// 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$).*)",
],
}