Files
obc-terrassement/app/admin/layout.tsx
Claude 1d0bd349fd feat: secure admin panel with Supabase auth + course management CRUD
- Replace ADMIN_SECRET query param with proper Supabase auth + is_admin flag
- Add admin layout with auth check (redirects non-admin to /)
- Add AdminShell component with sidebar navigation (Dashboard, Candidatures, Cours)
- Add admin dashboard with stats (candidatures, users, modules)
- Add admin candidatures page with filters and approve/reject
- Add admin course management page (create, edit, delete, publish/unpublish)
- Add API routes: GET/POST /api/admin/modules, GET/PUT/DELETE /api/admin/modules/[id]
- Add verifyAdmin() helper for API route protection
- Update database types with is_admin on profiles

https://claude.ai/code/session_01H2aRGDaKgarPvhay2HxN6Y
2026-02-10 13:25:58 +00:00

47 lines
1.1 KiB
TypeScript

import { redirect } from "next/navigation";
import { createClient, createAdminClient } from "@/lib/supabase/server";
import AdminShell from "@/components/admin/AdminShell";
import type { Profile } from "@/types/database.types";
export const runtime = "nodejs";
export default async function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const supabase = await createClient();
// Vérifier l'authentification
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
redirect("/login?redirect=/admin");
}
// Vérifier le statut admin via service role (pas de RLS)
const adminClient = createAdminClient();
const { data: profile } = await adminClient
.from("profiles")
.select("*")
.eq("id", user.id)
.single();
const typedProfile = profile as Profile | null;
if (!typedProfile || !typedProfile.is_admin) {
redirect("/");
}
return (
<AdminShell
adminName={typedProfile.full_name || "Admin"}
adminEmail={typedProfile.email}
>
{children}
</AdminShell>
);
}