chore: Implement route protection and security enhancements
- Add src/middleware.ts for global route proection - Whitelist public routes (/, /auth/*, /splits/[slug]) - Add redirect logic to Home page for returning users - Fix minor lint issues in Home page
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { createClient } from '@/lib/supabase/client';
|
||||
import BottleGrid from "@/components/BottleGrid";
|
||||
import AuthForm from "@/components/AuthForm";
|
||||
@@ -11,7 +11,7 @@ import { useI18n } from "@/i18n/I18nContext";
|
||||
import { useAuth } from "@/context/AuthContext";
|
||||
import { useSession } from "@/context/SessionContext";
|
||||
import TastingHub from "@/components/TastingHub";
|
||||
import { Sparkles, Loader2, Search, SlidersHorizontal } from "lucide-react";
|
||||
import { Sparkles, Loader2, Search, SlidersHorizontal, Settings, CircleUser } from "lucide-react";
|
||||
import { BottomNavigation } from '@/components/BottomNavigation';
|
||||
import ScanAndTasteFlow from '@/components/ScanAndTasteFlow';
|
||||
import UserStatusBadge from '@/components/UserStatusBadge';
|
||||
@@ -20,10 +20,12 @@ import SplitCard from '@/components/SplitCard';
|
||||
import HeroBanner from '@/components/HeroBanner';
|
||||
import QuickActionsGrid from '@/components/QuickActionsGrid';
|
||||
import DramOfTheDay from '@/components/DramOfTheDay';
|
||||
import { checkIsAdmin } from '@/services/track-api-usage';
|
||||
|
||||
export default function Home() {
|
||||
const supabase = createClient();
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [bottles, setBottles] = useState<any[]>([]);
|
||||
const { user, isLoading: isAuthLoading } = useAuth();
|
||||
const [isInternalLoading, setIsInternalLoading] = useState(false);
|
||||
|
||||
97
src/middleware.ts
Normal file
97
src/middleware.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { createServerClient } from "@supabase/ssr";
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
let response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
});
|
||||
|
||||
const supabase = createServerClient(
|
||||
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||
{
|
||||
cookies: {
|
||||
getAll() {
|
||||
return request.cookies.getAll();
|
||||
},
|
||||
setAll(cookiesToSet: any[]) {
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
request.cookies.set(name, value)
|
||||
);
|
||||
response = NextResponse.next({
|
||||
request: {
|
||||
headers: request.headers,
|
||||
},
|
||||
});
|
||||
cookiesToSet.forEach(({ name, value, options }) =>
|
||||
response.cookies.set(name, value, options)
|
||||
);
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Refresh session if expired - required for Server Components
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
const path = request.nextUrl.pathname;
|
||||
|
||||
// 1. Define Public Routes (Whitelist)
|
||||
const isPublic =
|
||||
path === "/" ||
|
||||
path.startsWith("/auth") || // Auth callbacks
|
||||
path.startsWith("/api") || // API routes (often handle their own auth or are public)
|
||||
path === "/manifest.webmanifest" ||
|
||||
path === "/sw.js" ||
|
||||
path === "/offline" ||
|
||||
path.startsWith("/icons") ||
|
||||
path.startsWith("/_next"); // Static assets
|
||||
|
||||
// 2. Specialized Logic for /splits
|
||||
// - Public: /splits/[slug]
|
||||
// - Protected: /splits/create, /splits/manage
|
||||
const isSplitsPublic =
|
||||
path.startsWith("/splits/") &&
|
||||
!path.startsWith("/splits/create") &&
|
||||
!path.startsWith("/splits/manage");
|
||||
|
||||
if (isPublic || isSplitsPublic) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// 3. Protected Routes
|
||||
// If no user, redirect to Home (which acts as Login)
|
||||
if (!user) {
|
||||
const redirectUrl = request.nextUrl.clone();
|
||||
redirectUrl.pathname = "/";
|
||||
// Add redirect param so Client can show Login Modal if needed?
|
||||
// Or just let them land on Home.
|
||||
// Ideally we'd persist the return URL:
|
||||
redirectUrl.searchParams.set("redirect_to", path);
|
||||
return NextResponse.redirect(redirectUrl);
|
||||
}
|
||||
|
||||
// 4. Admin Protection (Optional Layer in Middleware, but Page also checks)
|
||||
// We rely on Page component for Admin role check to avoid DB hit in middleware if possible,
|
||||
// OR we can trust getUser() + Page logic.
|
||||
// Middleware mainly ensures *Authentication*. Authorization is Page level.
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except for the ones starting with:
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico (favicon file)
|
||||
* Feel free to modify this pattern to include more paths.
|
||||
*/
|
||||
"/((?!_next/static|_next/image|favicon.ico).*)",
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user