Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 94 additions & 5 deletions frontend/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,103 @@ function getLocale(request: NextRequest): string | undefined {
}
}

/**
* Check if user is authenticated by verifying auth token in cookies
*/
function isAuthenticated(request: NextRequest): boolean {
const authToken = request.cookies.get('auth_token');
const sessionToken = request.cookies.get('session_token');

// Check for either auth_token or session_token
// You can customize this based on your authentication mechanism
return !!(authToken || sessionToken);
}

/**
* Extract locale from pathname
*/
function extractLocale(pathname: string): string | null {
const localeRegex = new RegExp(`^/(${i18n.locales.join('|')})`);
const match = pathname.match(localeRegex);
return match ? match[1] : null;
}

/**
* Get pathname without locale prefix
*/
function getPathnameWithoutLocale(pathname: string): string {
const locale = extractLocale(pathname);
if (locale) {
return pathname.replace(`/${locale}`, '') || '/';
}
return pathname;
}

/**
* Check if path is a public route (doesn't require authentication)
*/
function isPublicRoute(pathname: string): boolean {
const pathWithoutLocale = getPathnameWithoutLocale(pathname);

// Public routes that don't require authentication
const publicRoutes = [
'/login',
'/signup',
'/register',
'/forgot-password',
'/reset-password',
];

// Check if path starts with any public route
return publicRoutes.some(route => pathWithoutLocale.startsWith(route));
}

/**
* Check if path is a protected route (requires authentication)
* By default, all routes are protected except explicitly public ones
*/
function isProtectedRoute(pathname: string): boolean {
const pathWithoutLocale = getPathnameWithoutLocale(pathname);

// If it's a public route, it's not protected
if (isPublicRoute(pathname)) {
return false;
}

// All other routes (dashboard routes, home, etc.) are protected by default
// This includes: /, /my_books, /books, /profile, /settings, etc.
return true;
}

export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const authenticated = isAuthenticated(request);
const locale = extractLocale(pathname) || getLocale(request) || i18n.defaultLocale;
const pathWithoutLocale = getPathnameWithoutLocale(pathname);
const isPublic = isPublicRoute(pathname);
const isProtected = isProtectedRoute(pathname);

// Check if the path is missing the locale
// Handle locale routing first
const pathnameIsMissingLocale = i18n.locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
);

if (pathnameIsMissingLocale) {
// Get the locale (from cookie or fallback to browser preferences)
const locale = getLocale(request) || i18n.defaultLocale;
// Authentication checks (after locale extraction, before locale routing)
// If accessing a protected route without authentication, redirect to login
if (isProtected && !authenticated) {
const loginUrl = new URL(`/${locale}/login`, request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}

// If authenticated user tries to access auth pages (login, signup), redirect to home
if (authenticated && isPublic) {
const homeUrl = new URL(`/${locale}`, request.url);
return NextResponse.redirect(homeUrl);
}

// Handle locale prefixing
if (pathnameIsMissingLocale) {
// For default locale, rewrite internally (don't redirect)
if (locale === i18n.defaultLocale) {
const newUrl = request.nextUrl.clone();
Expand All @@ -77,9 +162,13 @@ export function middleware(request: NextRequest) {
return response;
}

// If path has a locale, just continue
// If path has a locale, continue
const response = NextResponse.next();
response.headers.set('x-pathname', pathname);

// Add authentication status to headers for server components
response.headers.set('x-authenticated', authenticated ? 'true' : 'false');

return response;
}

Expand Down