middleware
nahidbinwadood
typescript
3 months ago
26 kB
9
Indexable
// // middleware.ts
// import { match } from '@formatjs/intl-localematcher';
// import Negotiator from 'negotiator';
// import { NextRequestWithAuth, withAuth } from 'next-auth/middleware';
// import type { NextRequest } from 'next/server';
// import { NextResponse } from 'next/server';
// import { getSettingsData } from './app/[lang]/(public)/utils/getSettingsData';
// import { defaultLocale, locales } from './lib/i18n/config';
// export type UserData = {
// id: string;
// role: 'retailer' | 'admin';
// };
// /* ---------- helpers ---------- */
// function getLocale(request: NextRequest): string {
// const pathname = request.nextUrl.pathname;
// // 1) Check if locale is already in the URL path
// const pathnameLocale = locales.find(
// (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
// );
// if (pathnameLocale) return pathnameLocale;
// // 2) Check for locale cookie
// const cookieLocale = request.cookies.get('locale')?.value;
// if (cookieLocale && locales.includes(cookieLocale as any)) {
// return cookieLocale;
// }
// // 3) Fall back to Accept-Language header
// const acceptLanguage = request.headers.get('accept-language') ?? undefined;
// if (acceptLanguage) {
// const headers = { 'accept-language': acceptLanguage };
// const languages = new Negotiator({ headers }).languages();
// try {
// return match(languages, locales as any, defaultLocale);
// } catch {
// return defaultLocale;
// }
// }
// return defaultLocale;
// }
// function getUserFromToken(token: any | null): UserData | null {
// if (!token) return null;
// const userId = token.retailer?.id || token.admin?.id;
// if (!userId) return null;
// return {
// id: userId,
// role: token.userType as 'retailer' | 'admin',
// };
// }
// /* ---------- auth middleware ---------- */
// const authMiddleware = withAuth(
// async function middleware(request: NextRequestWithAuth) {
// const { pathname, search } = request.nextUrl;
// const tokenData = request.nextauth?.token as any | null;
// const user = getUserFromToken(tokenData);
// const isAuthenticated = !!user;
// const userRole = user?.role || null;
// let settingsData = null;
// if (userRole === 'retailer') {
// settingsData = (await getSettingsData(userRole as string)) || {};
// }
// // Extract locale from URL if present
// const urlLocale = locales.find(
// (locale) =>
// pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
// );
// // Check if the pathname already has a locale
// const pathnameHasLocale = !!urlLocale;
// // Get current locale from URL or detection
// const currentLocale = urlLocale || getLocale(request);
// // IMPORTANT FIX: Handle language switcher links properly
// // When user clicks on a language switcher link, preserve the intended locale
// const localeCookie = request.cookies.get('locale')?.value;
// // If pathname doesn't have a locale, we need to redirect to add one
// if (!pathnameHasLocale && pathname !== '/') {
// // For root path, let it fall through to the locale redirect logic below
// const newUrl = new URL(request.url);
// newUrl.pathname = `/${currentLocale}${pathname}`;
// const response = NextResponse.redirect(newUrl);
// // Set locale cookie
// response.cookies.set('locale', currentLocale, {
// httpOnly: true,
// secure: process.env.NODE_ENV === 'production',
// sameSite: 'lax',
// path: '/',
// maxAge: 60 * 60 * 24 * 30,
// });
// return response;
// }
// // Update locale cookie when locale changes in URL
// if (pathnameHasLocale && urlLocale) {
// // Check if we need to update the cookie
// if (localeCookie !== urlLocale) {
// const response = NextResponse.next();
// response.cookies.set('locale', urlLocale, {
// httpOnly: true,
// secure: process.env.NODE_ENV === 'production',
// sameSite: 'lax',
// path: '/',
// maxAge: 60 * 60 * 24 * 30,
// });
// return response;
// }
// }
// // Use the locale from URL or cookie
// const activeLocale = urlLocale || localeCookie || defaultLocale;
// // Only redirect admins from the root locale path to the admin dashboard
// if (
// userRole === 'admin' &&
// (pathname === `/${activeLocale}` ||
// pathname === `/${activeLocale}/` ||
// pathname === '/')
// ) {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/super-admin/plans`, request.url)
// );
// }
// // redirect to terms and condition==>
// if (pathname === `/${activeLocale}/terms`) {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/terms-and-conditions`, request.url)
// );
// }
// // redirect to faq==>
// if (pathname === `/${activeLocale}/faqs`) {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/faq`, request.url)
// );
// }
// // Handle forget-password redirects
// if (pathname.startsWith(`/${activeLocale}/forget-password`)) {
// if (isAuthenticated) {
// const redirectPaths = {
// retailer: `/${activeLocale}/retailer/dashboard`,
// admin: `/${activeLocale}/super-admin/plans`,
// };
// const redirectPath = redirectPaths[userRole || 'retailer'];
// if (redirectPath) {
// return NextResponse.redirect(new URL(redirectPath, request.url));
// }
// }
// }
// /* ---- prevent logged-in users from accessing auth pages ---- */
// if (
// isAuthenticated &&
// (pathname === `/${activeLocale}/auth/signin` ||
// pathname === `/${activeLocale}/auth/register` ||
// pathname === `/${activeLocale}/auth/admin/login`|| pathname === `/${activeLocale}/get-started`)
// ) {
// let redirectPath = `/${activeLocale}`;
// if (userRole === 'retailer')
// redirectPath = `/${activeLocale}/retailer/dashboard`;
// else if (userRole === 'admin')
// redirectPath = `/${activeLocale}/super-admin/plans`;
// return NextResponse.redirect(new URL(redirectPath, request.url));
// }
// /* ---- maintenance mode ---- */
// if (
// pathname.startsWith(`/${activeLocale}/maintenance`) &&
// !settingsData?.isUnderMaintenance
// ) {
// return NextResponse.redirect(new URL(`/${activeLocale}/`, request.url));
// }
// /* ---- protect /:locale/retailer routes - only retailers can access ---- */
// if (pathname.startsWith(`/${activeLocale}/retailer`)) {
// if (settingsData?.isUnderMaintenance && userRole === 'retailer') {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/maintenance`, request.url)
// );
// }
// if (!isAuthenticated) {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/auth/signin`, request.url)
// );
// }
// // Only retailers can access retailer routes
// if (userRole !== 'retailer') {
// // Redirect admins trying to access retailer routes to admin dashboard
// if (userRole === 'admin') {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/super-admin/plans`, request.url)
// );
// }
// // Redirect unauthenticated or unknown roles to login
// return NextResponse.redirect(
// new URL(`/${activeLocale}/auth/signin`, request.url)
// );
// }
// }
// /* ---- protect /:locale/super-admin routes - only admins can access ---- */
// if (pathname.startsWith(`/${activeLocale}/super-admin`)) {
// if (!isAuthenticated) {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/auth/admin/login`, request.url)
// );
// }
// // Only admins can access admin routes
// if (userRole !== 'admin') {
// // Redirect retailers trying to access admin routes to retailer dashboard
// if (userRole === 'retailer') {
// return NextResponse.redirect(
// new URL(`/${activeLocale}/retailer/dashboard`, request.url)
// );
// }
// // Redirect unauthenticated or unknown roles to login
// return NextResponse.redirect(
// new URL(`/${activeLocale}/auth/signin`, request.url)
// );
// }
// }
// // If we reach here and still don't have a locale in the path, redirect to add locale
// if (!pathnameHasLocale && pathname !== '/') {
// const locale = getLocale(request);
// const newUrl = new URL(request.url);
// newUrl.pathname = `/${locale}${pathname}`;
// const response = NextResponse.redirect(newUrl);
// response.cookies.set('locale', locale, {
// httpOnly: true,
// secure: process.env.NODE_ENV === 'production',
// sameSite: 'lax',
// path: '/',
// maxAge: 60 * 60 * 24 * 30,
// });
// return response;
// }
// // For root path without locale, redirect to locale-prefixed root
// if (pathname === '/') {
// const locale = getLocale(request);
// const newUrl = new URL(request.url);
// newUrl.pathname = `/${locale}`;
// const response = NextResponse.redirect(newUrl);
// response.cookies.set('locale', locale, {
// httpOnly: true,
// secure: process.env.NODE_ENV === 'production',
// sameSite: 'lax',
// path: '/',
// maxAge: 60 * 60 * 24 * 30,
// });
// return response;
// }
// return NextResponse.next();
// },
// {
// callbacks: {
// authorized: ({ token, req }) => {
// const { pathname } = req.nextUrl;
// // Skip static / internal paths
// if (
// pathname.startsWith('/_next') ||
// pathname.startsWith('/api') ||
// pathname.includes('.') ||
// pathname.startsWith('/favicon')
// ) {
// return true;
// }
// // Extract locale from pathname
// const urlLocale =
// locales.find(
// (locale) =>
// pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
// ) || defaultLocale;
// // Allow access to auth pages and public pages
// if (
// pathname === `/${urlLocale}/auth/signin` ||
// pathname === `/${urlLocale}/auth/register` ||
// pathname === `/${urlLocale}/auth/admin/login` ||
// pathname === `/${urlLocale}` ||
// pathname === `/${urlLocale}/` ||
// pathname === '/' ||
// pathname.startsWith(`/${urlLocale}/pricing`) || // Add pricing to public routes
// pathname.startsWith(`/${urlLocale}/about`) || // Add other public routes as needed
// pathname.startsWith(`/${urlLocale}/contact`)
// ) {
// return true;
// }
// // Require authentication for protected routes
// if (
// pathname.startsWith(`/${urlLocale}/retailer`) ||
// pathname.startsWith(`/${urlLocale}/super-admin`)
// ) {
// return !!token;
// }
// // Allow all other routes
// return true;
// },
// },
// pages: {
// signIn: `/${defaultLocale}/auth/signin`,
// error: `/${defaultLocale}/auth/signin`,
// },
// }
// );
// /* ---------- main middleware export ---------- */
// export default authMiddleware;
// /* ---------- matcher ---------- */
// export const config = {
// matcher: [
// '/((?!api|_next/static|_next/image|favicon.ico|images|icons|logo|assets|sw\\.js).*)',
// ],
// };
// for only english --->>
// middleware.ts
import { NextRequestWithAuth, withAuth } from 'next-auth/middleware';
import { NextResponse } from 'next/server';
import { getSettingsData } from './app/[lang]/(public)/utils/getSettingsData';
// Force English only for first release
const LOCKED_LOCALE = 'en';
export type UserData = {
id: string;
role: 'retailer' | 'admin';
};
/* ---------- helpers ---------- */
function getUserFromToken(token: any | null): UserData | null {
if (!token) return null;
const userId = token.retailer?.id || token.admin?.id;
if (!userId) return null;
return {
id: userId,
role: token.userType as 'retailer' | 'admin',
};
}
/* ---------- auth middleware ---------- */
const authMiddleware = withAuth(
async function middleware(request: NextRequestWithAuth) {
const { pathname } = request.nextUrl;
const tokenData = request.nextauth?.token as any | null;
const user = getUserFromToken(tokenData);
const isAuthenticated = !!user;
const userRole = user?.role || null;
let settingsData = null;
if (userRole === 'retailer') {
settingsData = (await getSettingsData(userRole as string)) || {};
}
// PRIORITY 1: Handle root path FIRST - redirect to /en
if (pathname === '/') {
const newUrl = new URL(request.url);
newUrl.pathname = `/${LOCKED_LOCALE}`;
const response = NextResponse.redirect(newUrl);
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
return response;
}
// PRIORITY 2: Check if pathname has a locale prefix that's NOT 'en'
const otherLocalePattern = /^\/(?!en\/)([a-z]{2})\//i;
const hasOtherLocale = otherLocalePattern.test(pathname);
if (hasOtherLocale) {
// Extract the path without the wrong locale
const pathWithoutLocale = pathname.replace(/^\/[a-z]{2}/, '');
const newUrl = new URL(request.url);
newUrl.pathname = `/${LOCKED_LOCALE}${pathWithoutLocale}`;
const response = NextResponse.redirect(newUrl);
// Set locale cookie to 'en'
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365, // 1 year
});
return response;
}
// redirect to terms and condition==>
if (pathname === `/${LOCKED_LOCALE}/terms`) {
return NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/terms-and-conditions`, request.url)
);
}
// redirect to faqs==>
if (pathname === `/${LOCKED_LOCALE}/faqs`) {
return NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/faq`, request.url)
);
}
// PRIORITY 3: Check if pathname starts with /en/ or is /en
const hasEnLocale =
pathname.startsWith(`/${LOCKED_LOCALE}/`) ||
pathname === `/${LOCKED_LOCALE}`;
// If no locale prefix at all, add /en
if (!hasEnLocale) {
const newUrl = new URL(request.url);
newUrl.pathname = `/${LOCKED_LOCALE}${pathname}`;
const response = NextResponse.redirect(newUrl);
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
return response;
}
// Ensure locale cookie is always set to 'en'
const localeCookie = request.cookies.get('locale')?.value;
const shouldUpdateCookie = localeCookie !== LOCKED_LOCALE;
// Redirect admins from /en to admin dashboard
if (
userRole === 'admin' &&
(pathname === `/${LOCKED_LOCALE}` || pathname === `/${LOCKED_LOCALE}/`)
) {
const response = NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/super-admin/plans`, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
// Handle forget-password redirects
if (pathname.startsWith(`/${LOCKED_LOCALE}/forget-password`)) {
if (isAuthenticated) {
const redirectPaths = {
retailer: `/${LOCKED_LOCALE}/retailer/dashboard`,
admin: `/${LOCKED_LOCALE}/super-admin/plans`,
};
const redirectPath = redirectPaths[userRole || 'retailer'];
if (redirectPath) {
const response = NextResponse.redirect(
new URL(redirectPath, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
}
}
// Handle verification redirects
if (pathname.startsWith(`/${LOCKED_LOCALE}/auth/verification`)) {
if (userRole === 'retailer' || userRole === 'admin') {
const redirectPaths = {
retailer: `/${LOCKED_LOCALE}/retailer/dashboard`,
admin: `/${LOCKED_LOCALE}/super-admin/plans`,
};
const redirectPath = redirectPaths[userRole || 'retailer'];
if (redirectPath) {
const response = NextResponse.redirect(
new URL(redirectPath, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
}
}
/* ---- prevent logged-in users from accessing auth pages ---- */
if (
isAuthenticated &&
(pathname === `/${LOCKED_LOCALE}/auth/signin` ||
pathname === `/${LOCKED_LOCALE}/auth/signup` ||
pathname === `/${LOCKED_LOCALE}/auth/admin/login` ||
pathname === `/${LOCKED_LOCALE}/get-started`)
) {
let redirectPath = `/${LOCKED_LOCALE}`;
if (userRole === 'retailer')
redirectPath = `/${LOCKED_LOCALE}/retailer/dashboard`;
else if (userRole === 'admin')
redirectPath = `/${LOCKED_LOCALE}/super-admin/dashboard`;
const response = NextResponse.redirect(
new URL(redirectPath, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
/* ---- maintenance mode ---- */
if (
pathname.startsWith(`/${LOCKED_LOCALE}/maintenance`) &&
!settingsData?.isUnderMaintenance
) {
const response = NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/`, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
/* ---- protect /en/retailer routes - only retailers can access ---- */
if (pathname.startsWith(`/${LOCKED_LOCALE}/retailer`)) {
if (settingsData?.isUnderMaintenance && userRole === 'retailer') {
const response = NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/maintenance`, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
if (!isAuthenticated) {
const response = NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/auth/signin`, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
// Only retailers can access retailer routes
if (userRole !== 'retailer') {
const redirectUrl =
userRole === 'admin'
? `/${LOCKED_LOCALE}/super-admin/plans`
: `/${LOCKED_LOCALE}/auth/signin`;
const response = NextResponse.redirect(
new URL(redirectUrl, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
}
/* ---- protect /en/super-admin routes - only admins can access ---- */
if (pathname.startsWith(`/${LOCKED_LOCALE}/super-admin`)) {
if (!isAuthenticated) {
const response = NextResponse.redirect(
new URL(`/${LOCKED_LOCALE}/auth/admin/login`, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
// Only admins can access admin routes
if (userRole !== 'admin') {
const redirectUrl =
userRole === 'retailer'
? `/${LOCKED_LOCALE}/retailer/dashboard`
: `/${LOCKED_LOCALE}/auth/signin`;
const response = NextResponse.redirect(
new URL(redirectUrl, request.url)
);
if (shouldUpdateCookie) {
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
}
return response;
}
}
// Default response - update cookie if needed
if (shouldUpdateCookie) {
const response = NextResponse.next();
response.cookies.set('locale', LOCKED_LOCALE, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 365,
});
return response;
}
return NextResponse.next();
},
{
callbacks: {
authorized: ({ token, req }) => {
const { pathname } = req.nextUrl;
// Skip static / internal paths
if (
pathname.startsWith('/_next') ||
pathname.startsWith('/api') ||
pathname.includes('.') ||
pathname.startsWith('/favicon')
) {
return true;
}
// Allow access to auth pages and public pages
if (
pathname === `/${LOCKED_LOCALE}/auth/signin` ||
pathname === `/${LOCKED_LOCALE}/auth/register` ||
pathname === `/${LOCKED_LOCALE}/auth/admin/login` ||
pathname === `/${LOCKED_LOCALE}` ||
pathname === `/${LOCKED_LOCALE}/` ||
pathname === '/' ||
pathname.startsWith(`/${LOCKED_LOCALE}/pricing`) ||
pathname.startsWith(`/${LOCKED_LOCALE}/about`) ||
pathname.startsWith(`/${LOCKED_LOCALE}/contact`) ||
pathname.startsWith(`/${LOCKED_LOCALE}/maintenance`) ||
pathname.startsWith(`/${LOCKED_LOCALE}/forget-password`)
) {
return true;
}
// Require authentication for protected routes
if (
pathname.startsWith(`/${LOCKED_LOCALE}/retailer`) ||
pathname.startsWith(`/${LOCKED_LOCALE}/super-admin`)
) {
return !!token;
}
// Allow all other routes
return true;
},
},
pages: {
signIn: `/${LOCKED_LOCALE}/auth/signin`,
error: `/${LOCKED_LOCALE}/auth/signin`,
},
}
);
/* ---------- main middleware export ---------- */
export default authMiddleware;
/* ---------- matcher ---------- */
export const config = {
matcher: [
'/((?!api|_next/static|_next/image|favicon.ico|images|icons|logo|assets|sw\\.js).*)',
],
};
Editor is loading...
Leave a Comment