/opt/canhelp/apps/web/src
Edit: /opt/canhelp/apps/web/src/middleware.ts (2073B)
import { NextRequest, NextResponse } from 'next/server'
// Module-level cache for maintenance check — Edge runtime doesn't support Next.js data cache
let maintenanceCached: boolean | null = null
let maintenanceCacheExpiry = 0
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
// Pass pathname to layouts via request header
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-pathname', pathname)
// Skip admin, login/auth, maintenance page itself, API, static assets, and SEO files
if (
pathname.startsWith('/admin') ||
pathname.startsWith('/login') ||
pathname.startsWith('/register') ||
pathname.startsWith('/auth') ||
pathname.startsWith('/maintenance') ||
pathname.startsWith('/api') ||
pathname.startsWith('/_next') ||
pathname.startsWith('/fonts') ||
pathname === '/sitemap.xml' ||
pathname === '/robots.txt' ||
pathname.match(/\.(ico|png|jpg|jpeg|svg|webp|css|js|xml|txt)$/)
) {
return NextResponse.next({ request: { headers: requestHeaders } })
}
try {
const now = Date.now()
let isMaintenance: boolean
if (now < maintenanceCacheExpiry && maintenanceCached !== null) {
isMaintenance = maintenanceCached
} else {
const apiBase = process.env.INTERNAL_API_URL || 'http://localhost:4000'
const res = await fetch(`${apiBase}/api/admin/seo-public`)
if (res.ok) {
const settings: Record
= await res.json()
isMaintenance = settings['seo.maintenanceMode'] === 'true'
maintenanceCached = isMaintenance
maintenanceCacheExpiry = now + 30_000
} else {
isMaintenance = false
}
}
if (isMaintenance) {
return NextResponse.redirect(new URL('/maintenance', request.url))
}
} catch {
// If API is unreachable, allow access — don't block the site
}
return NextResponse.next({ request: { headers: requestHeaders } })
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}