diff --git a/src/lib/user-admin.functions.ts b/src/lib/user-admin.functions.ts new file mode 100644 index 0000000..cf75349 --- /dev/null +++ b/src/lib/user-admin.functions.ts @@ -0,0 +1,52 @@ +import { createServerFn } from "@tanstack/react-start"; +import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware"; + +type Role = "admin" | "teacher" | "parent" | "student"; + +// Admin-only: create a user account (service-role, server-side), set its role, +// and optionally link it to a student (for parent hand-off). +export const createUserFn = createServerFn({ method: "POST" }) + .middleware([requireSupabaseAuth]) + .validator((d: { fullName: string; email: string; password: string; role: Role; linkStudentId?: string }) => d) + .handler(async ({ data, context }) => { + const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); + const callerId = (context as { userId: string }).userId; + + const { data: adminRow } = await supabaseAdmin + .from("user_roles").select("role").eq("user_id", callerId).eq("role", "admin").maybeSingle(); + if (!adminRow) throw new Error("Only admins can add users."); + + const email = data.email.trim().toLowerCase(); + if (!email || !data.fullName.trim()) throw new Error("Name and email are required."); + + const { data: created, error } = await supabaseAdmin.auth.admin.createUser({ + email, + password: data.password, + email_confirm: true, + user_metadata: { full_name: data.fullName.trim() }, + }); + if (error) throw new Error(error.message); + const uid = created.user?.id; + if (!uid) throw new Error("User creation failed."); + + // The on_auth_user_created trigger assigns 'parent' by default; adjust to the chosen role. + if (data.role !== "parent") { + await supabaseAdmin.from("user_roles").delete().eq("user_id", uid).eq("role", "parent"); + } + const { error: rErr } = await supabaseAdmin + .from("user_roles").upsert({ user_id: uid, role: data.role }, { onConflict: "user_id,role" }); + if (rErr) throw new Error(rErr.message); + + if (data.linkStudentId && data.role === "parent") { + const { error: lErr } = await supabaseAdmin + .from("parent_students").upsert({ parent_id: uid, student_id: data.linkStudentId }, { onConflict: "parent_id,student_id" }); + if (lErr) throw new Error(lErr.message); + } + if (data.linkStudentId && data.role === "student") { + const { error: sErr } = await supabaseAdmin + .from("students").update({ user_id: uid }).eq("id", data.linkStudentId); + if (sErr) throw new Error(sErr.message); + } + + return { id: uid, email }; + });