-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmiddleware.ts
More file actions
123 lines (106 loc) · 3.58 KB
/
middleware.ts
File metadata and controls
123 lines (106 loc) · 3.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* Next.js Middleware - Internationalization & Route Protection
*
* Handles:
* 1. Locale detection and routing (next-intl)
* 2. Authentication and route protection (NextAuth)
*/
import createMiddleware from 'next-intl/middleware';
import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
import { locales, defaultLocale } from './i18n';
// Create next-intl middleware
const intlMiddleware = createMiddleware({
locales: locales,
defaultLocale: defaultLocale,
localePrefix: 'always', // Always show locale in URL (/en/... or /sl/...)
localeDetection: true, // Auto-detect locale from browser
});
export async function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
// Skip authentication check for API routes and NextAuth routes
if (pathname.startsWith('/api')) {
return NextResponse.next();
}
// Skip authentication check for public assets
if (
pathname.startsWith('/_next') ||
pathname.startsWith('/favicon.ico') ||
pathname.startsWith('/robots.txt')
) {
return NextResponse.next();
}
// Apply locale middleware first
const response = intlMiddleware(req);
// Check if path includes authentication pages (public pages)
if (
pathname.includes('/login') ||
pathname.includes('/register') ||
pathname.includes('/staff-register') ||
pathname.includes('/forgot-password') ||
pathname.includes('/reset-password')
) {
return response;
}
// For all other routes, check authentication
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
if (!token) {
// Extract locale from pathname
const localeMatch = pathname.match(/^\/(en|sl)\//);
const locale = localeMatch ? localeMatch[1] : defaultLocale;
// Redirect to login page with callback URL
const loginUrl = new URL(`/${locale}/login`, req.url);
loginUrl.searchParams.set('callbackUrl', pathname);
return NextResponse.redirect(loginUrl);
}
// Role-based routing
const localeMatch = pathname.match(/^\/(en|sl)\//);
const locale = localeMatch ? localeMatch[1] : defaultLocale;
const userRole = token.role as string;
// Production routes that STAFF should not access
const productionRoutes = [
'/dashboard',
'/orders',
'/worksheets',
'/invoices',
'/dentists',
'/materials',
'/pricing',
'/quality-control',
'/settings'
];
const isStaffRoute = pathname.includes('/staff');
const isProductionRoute = productionRoutes.some(route => pathname.includes(route));
// STAFF users
if (userRole === 'STAFF') {
// Block access to production routes
if (isProductionRoute && !isStaffRoute) {
const staffDashboardUrl = new URL(`/${locale}/staff/dashboard`, req.url);
return NextResponse.redirect(staffDashboardUrl);
}
}
// Production users (ADMIN, TECHNICIAN, QC_INSPECTOR, INVOICING)
else {
// Block access to staff-only routes
if (isStaffRoute) {
const dashboardUrl = new URL(`/${locale}/dashboard`, req.url);
return NextResponse.redirect(dashboardUrl);
}
// TECHNICIAN: redirect /dashboard to /production (their home page)
if (userRole === 'TECHNICIAN' && pathname.match(/^\/(en|sl)\/dashboard$/)) {
return NextResponse.redirect(new URL(`/${locale}/production`, req.url));
}
}
return response;
}
export const config = {
matcher: [
/*
* Match all request paths except:
* - /api (API routes)
* - /_next (Next.js internals)
* - /favicon.ico, /robots.txt (public files)
*/
'/((?!api|_next/static|_next/image|favicon.ico|robots.txt).*)',
],
};