Files
imphnen-frontend-service/apps/hackathon/src/middleware.ts
T

143 lines
4.0 KiB
TypeScript
Raw Normal View History

2025-11-25 01:47:56 +07:00
import { SessionUser } from '@imphnen-frontend-service/utils';
import { supabase } from '@imphnen-frontend-service/service';
2025-11-24 12:51:41 +07:00
import { LoaderFunctionArgs, redirect } from 'react-router';
const mappingPublicRoutes = [
'/',
];
2025-11-25 01:47:56 +07:00
const mappingOnboardingRoutes = [
'/onboarding/user',
];
2025-11-24 12:51:41 +07:00
const mappingRoutePermissions = [
{
path: '/dashboard',
permissions: [],
},
2025-11-25 01:47:56 +07:00
{
path: '/teams/browse',
permissions: [],
},
{
path: '/teams/create',
permissions: [],
},
2025-11-24 12:51:41 +07:00
];
const mappingPublicPrefixRoutes = [
'/hackathons',
];
2025-11-25 01:47:56 +07:00
// Cache to prevent redundant checks (cache for 5 seconds)
const onboardingCache = new Map<string, { hasLocation: boolean; timestamp: number }>();
const CACHE_DURATION = 5000; // 5 seconds
2025-11-24 12:51:41 +07:00
export const middleware = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const pathname = url.pathname;
2025-11-25 01:47:56 +07:00
console.log('[Middleware] Checking route:', pathname);
// Get session from Supabase (authoritative source)
const { data: { session: supabaseSession }, error: sessionError } = await supabase.auth.getSession();
// Handle session errors
if (sessionError) {
console.error('[Middleware] Session error:', sessionError);
// Don't redirect on session errors, let the app handle it
}
2025-11-24 12:51:41 +07:00
// Allow to access the hackathon pages without authentication
if (mappingPublicPrefixRoutes.some((prefix) => pathname.startsWith(prefix))) {
return null;
}
2025-11-25 01:47:56 +07:00
// Public routes - redirect to dashboard if already authenticated
2025-11-24 12:51:41 +07:00
if (mappingPublicRoutes.includes(pathname)) {
2025-11-25 01:47:56 +07:00
if (supabaseSession) return redirect('/dashboard');
2025-11-24 12:51:41 +07:00
return null;
}
2025-11-25 01:47:56 +07:00
// Auth callback - allow without authentication check (for OAuth callback)
if (pathname === '/auth/callback') {
return null;
}
// Auth routes (all /auth/* paths) - redirect to dashboard if already authenticated
if (pathname.startsWith('/auth')) {
if (supabaseSession) return redirect('/dashboard');
return null;
}
// Require authentication for all other routes - ONLY check Supabase session
if (!supabaseSession) {
return redirect('/auth/login');
}
// Check if user has completed onboarding by querying database (not localStorage!)
// Skip onboarding check for onboarding routes themselves
if (!mappingOnboardingRoutes.includes(pathname)) {
try {
const userId = supabaseSession.user.id;
const now = Date.now();
// Check cache first
const cached = onboardingCache.get(userId);
let hasLocation = false;
if (cached && (now - cached.timestamp) < CACHE_DURATION) {
console.log('[Middleware] Using cached onboarding status');
hasLocation = cached.hasLocation;
} else {
console.log('[Middleware] Fetching fresh onboarding status');
const { data: userData, error: userError } = await supabase
.from('users')
.select('location')
.eq('id', userId)
.single();
if (userError) {
console.error('[Middleware] Failed to fetch user data:', userError);
// If we can't fetch user data, allow access (don't break the app)
return null;
}
hasLocation = !!userData?.location;
// Update cache
onboardingCache.set(userId, { hasLocation, timestamp: now });
}
if (!hasLocation) {
return redirect('/onboarding/user');
}
} catch (error) {
console.error('[Middleware] Unexpected error checking onboarding:', error);
// On error, allow access (fail open)
return null;
}
}
// Check route permissions using fresh user data from Zustand (for UI metadata)
const session = SessionUser.get();
const userPermissions =
session?.role?.permissions?.map?.((perm) => perm?.name) ?? [];
2025-11-24 12:51:41 +07:00
const matchedRoute = mappingRoutePermissions.find(
(route) => route.path === pathname
);
if (matchedRoute) {
const hasPermission =
!matchedRoute.permissions ||
matchedRoute.permissions.some((perm) => userPermissions.includes(perm));
if (!hasPermission) {
2025-11-25 01:47:56 +07:00
return redirect('/dashboard');
2025-11-24 12:51:41 +07:00
}
}
return null;
};