mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 22:04:10 +00:00
- Add Products collection with comprehensive fields (pricing, inventory, SEO, CTA) - Add ProductCategories collection with hierarchical structure - Implement CI/CD pipeline with GitHub Actions (lint, typecheck, test, build, e2e) - Add access control test utilities and unit tests - Fix Posts API to include category field for backwards compatibility - Update ESLint config with ignores for migrations and admin components - Add centralized access control functions in src/lib/access - Add db-direct.sh utility script for database access 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
// src/middleware.ts
|
|
// Next.js Middleware for locale detection and routing
|
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
import { defaultLocale, isValidLocale, type Locale } from '@/lib/i18n'
|
|
|
|
// Paths that should not be affected by locale routing
|
|
const PUBLIC_FILE = /\.(.*)$/
|
|
const EXCLUDED_PATHS = ['/admin', '/api', '/_next', '/favicon.ico', '/robots.txt', '/sitemap.xml']
|
|
|
|
/**
|
|
* Detect user's preferred locale from Accept-Language header
|
|
*/
|
|
function getPreferredLocale(request: NextRequest): Locale {
|
|
const acceptLanguage = request.headers.get('accept-language')
|
|
|
|
if (!acceptLanguage) {
|
|
return defaultLocale
|
|
}
|
|
|
|
// Parse Accept-Language header
|
|
const languages = acceptLanguage
|
|
.split(',')
|
|
.map((lang) => {
|
|
const [code, priority] = lang.trim().split(';q=')
|
|
return {
|
|
code: code.split('-')[0].toLowerCase(), // Get primary language code
|
|
priority: priority ? parseFloat(priority) : 1,
|
|
}
|
|
})
|
|
.sort((a, b) => b.priority - a.priority)
|
|
|
|
// Find first matching locale
|
|
for (const { code } of languages) {
|
|
if (isValidLocale(code)) {
|
|
return code
|
|
}
|
|
}
|
|
|
|
return defaultLocale
|
|
}
|
|
|
|
/**
|
|
* Get locale from cookie
|
|
*/
|
|
function getLocaleFromCookie(request: NextRequest): Locale | null {
|
|
const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value
|
|
|
|
if (cookieLocale && isValidLocale(cookieLocale)) {
|
|
return cookieLocale
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
export function middleware(request: NextRequest) {
|
|
const { pathname } = request.nextUrl
|
|
|
|
// Skip locale routing for excluded paths and public files
|
|
if (
|
|
EXCLUDED_PATHS.some((path) => pathname.startsWith(path)) ||
|
|
PUBLIC_FILE.test(pathname)
|
|
) {
|
|
return NextResponse.next()
|
|
}
|
|
|
|
// Check if pathname already has a valid locale prefix
|
|
const pathnameLocale = pathname.split('/')[1]
|
|
|
|
if (isValidLocale(pathnameLocale)) {
|
|
// Valid locale in URL, set cookie and continue
|
|
const response = NextResponse.next()
|
|
response.cookies.set('NEXT_LOCALE', pathnameLocale, {
|
|
maxAge: 60 * 60 * 24 * 365, // 1 year
|
|
path: '/',
|
|
})
|
|
return response
|
|
}
|
|
|
|
// No locale in URL, redirect to preferred locale
|
|
const cookieLocale = getLocaleFromCookie(request)
|
|
const preferredLocale = cookieLocale || getPreferredLocale(request)
|
|
|
|
// Build new URL with locale prefix
|
|
const newUrl = new URL(request.url)
|
|
newUrl.pathname = `/${preferredLocale}${pathname === '/' ? '' : pathname}`
|
|
|
|
// Redirect to localized URL
|
|
const response = NextResponse.redirect(newUrl)
|
|
response.cookies.set('NEXT_LOCALE', preferredLocale, {
|
|
maxAge: 60 * 60 * 24 * 365, // 1 year
|
|
path: '/',
|
|
})
|
|
|
|
return response
|
|
}
|
|
|
|
export const config = {
|
|
// Match all paths except static files and API routes
|
|
matcher: ['/((?!api|_next/static|_next/image|admin|favicon.ico).*)'],
|
|
}
|