diff --git a/app/api/admin/setup/route.ts b/app/api/admin/setup/route.ts new file mode 100644 index 0000000..267201e --- /dev/null +++ b/app/api/admin/setup/route.ts @@ -0,0 +1,86 @@ +import { NextResponse } from "next/server"; +import { createAdminClient } from "@/lib/supabase/server"; + +export const runtime = "nodejs"; + +// POST /api/admin/setup - Créer le premier compte admin +// Ne fonctionne QUE s'il n'existe aucun admin dans la base +export async function POST(request: Request) { + const supabase = createAdminClient(); + + // Vérifier qu'aucun admin n'existe + const { data: existingAdmins } = await supabase + .from("profiles") + .select("id") + .eq("is_admin", true); + + if (existingAdmins && existingAdmins.length > 0) { + return NextResponse.json( + { error: "Un compte admin existe déjà. Cette route est désactivée." }, + { status: 403 } + ); + } + + const body = await request.json(); + const { email, password, full_name } = body; + + if (!email || !password) { + return NextResponse.json({ error: "Email et mot de passe requis." }, { status: 400 }); + } + + if (password.length < 8) { + return NextResponse.json({ error: "Le mot de passe doit contenir au moins 8 caractères." }, { status: 400 }); + } + + // Créer le compte auth Supabase + const { data: authUser, error: authError } = await supabase.auth.admin.createUser({ + email, + password, + email_confirm: true, + user_metadata: { full_name: full_name || "Admin" }, + }); + + if (authError) { + console.error("Erreur création admin:", authError); + return NextResponse.json({ error: authError.message }, { status: 500 }); + } + + if (!authUser.user) { + return NextResponse.json({ error: "Erreur lors de la création du compte." }, { status: 500 }); + } + + // Mettre à jour le profil en admin + // Le profil est normalement créé par un trigger Supabase + // On attend un instant puis on le met à jour + // Si pas de trigger, on le crée manuellement + const { data: existingProfile } = await supabase + .from("profiles") + .select("id") + .eq("id", authUser.user.id) + .single(); + + if (existingProfile) { + await supabase + .from("profiles") + .update({ + is_admin: true, + full_name: full_name || "Admin", + subscription_status: "active", + } as never) + .eq("id", authUser.user.id); + } else { + // Créer le profil manuellement + await supabase.from("profiles").insert({ + id: authUser.user.id, + email, + full_name: full_name || "Admin", + is_admin: true, + subscription_status: "active", + } as never); + } + + return NextResponse.json({ + success: true, + message: "Compte admin créé avec succès ! Connecte-toi sur /login puis va sur /admin.", + }); +} diff --git a/app/login/page.tsx b/app/login/page.tsx index 4b448e9..67c0d06 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -21,7 +21,7 @@ export default function LoginPage() { try { const supabase = createClient(); - const { error: authError } = await supabase.auth.signInWithPassword({ + const { data: authData, error: authError } = await supabase.auth.signInWithPassword({ email, password, }); @@ -35,6 +35,21 @@ export default function LoginPage() { return; } + // Vérifier si l'utilisateur est admin pour la redirection + if (authData.user) { + const { data: profile } = await supabase + .from("profiles") + .select("is_admin") + .eq("id", authData.user.id) + .single(); + + if (profile && (profile as { is_admin?: boolean }).is_admin) { + router.push("/admin"); + router.refresh(); + return; + } + } + router.push("/dashboard"); router.refresh(); } catch { diff --git a/app/setup-admin/page.tsx b/app/setup-admin/page.tsx new file mode 100644 index 0000000..f01ee94 --- /dev/null +++ b/app/setup-admin/page.tsx @@ -0,0 +1,145 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; + +export default function AdminSetupPage() { + const [fullName, setFullName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(false); + + const handleSetup = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(null); + + try { + const res = await fetch("/api/admin/setup", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password, full_name: fullName }), + }); + + const data = await res.json(); + if (!res.ok) throw new Error(data.error); + + setSuccess(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Erreur lors de la création."); + } finally { + setLoading(false); + } + }; + + if (success) { + return ( +
+
+
+
+ + + +
+

Compte admin créé !

+

+ Ton compte admin a été créé avec succès. Connecte-toi pour accéder au panel d'administration. +

+ + Se connecter + +
+
+
+ ); + } + + return ( +
+
+
+
+
+ H +
+ + HookLab + +
+

Configuration admin

+

+ Crée ton compte administrateur. Cette page ne fonctionne qu'une seule fois. +

+
+ +
+
+
+ + setFullName(e.target.value)} + required + className="w-full px-4 py-3 bg-dark-lighter border border-dark-border rounded-xl text-white placeholder-white/30 text-sm focus:outline-none focus:border-primary" + /> +
+
+ + setEmail(e.target.value)} + required + className="w-full px-4 py-3 bg-dark-lighter border border-dark-border rounded-xl text-white placeholder-white/30 text-sm focus:outline-none focus:border-primary" + /> +
+
+ + setPassword(e.target.value)} + required + minLength={8} + className="w-full px-4 py-3 bg-dark-lighter border border-dark-border rounded-xl text-white placeholder-white/30 text-sm focus:outline-none focus:border-primary" + /> +
+ + {error && ( +
+

{error}

+
+ )} + + +
+
+
+
+ ); +}