middleware

 avatar
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