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
This commit is contained in:
40
lib/admin.ts
Normal file
40
lib/admin.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { createClient, createAdminClient } from "@/lib/supabase/server";
|
||||
import type { Profile } from "@/types/database.types";
|
||||
|
||||
// Vérifie que l'utilisateur connecté est admin
|
||||
// Utilisé dans les API routes admin
|
||||
export async function verifyAdmin(): Promise<{ admin: Profile } | { error: string; status: number }> {
|
||||
const supabase = await createClient();
|
||||
|
||||
const {
|
||||
data: { user },
|
||||
error: authError,
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (authError || !user) {
|
||||
return { error: "Non authentifié.", status: 401 };
|
||||
}
|
||||
|
||||
// Utiliser le client admin pour lire le profil (pas de RLS)
|
||||
const adminClient = createAdminClient();
|
||||
const { data: profile, error: profileError } = await adminClient
|
||||
.from("profiles")
|
||||
.select("*")
|
||||
.eq("id", user.id)
|
||||
.single();
|
||||
|
||||
if (profileError || !profile) {
|
||||
return { error: "Profil introuvable.", status: 404 };
|
||||
}
|
||||
|
||||
if (!(profile as Profile).is_admin) {
|
||||
return { error: "Accès refusé.", status: 403 };
|
||||
}
|
||||
|
||||
return { admin: profile as Profile };
|
||||
}
|
||||
|
||||
// Helper pour répondre avec erreur si non-admin
|
||||
export function isAdminError(result: { admin: Profile } | { error: string; status: number }): result is { error: string; status: number } {
|
||||
return "error" in result;
|
||||
}
|
||||
Reference in New Issue
Block a user