diff --git a/src/app/FamilyProvider.tsx b/src/app/FamilyProvider.tsx index 72d0ba0..2f71af8 100644 --- a/src/app/FamilyProvider.tsx +++ b/src/app/FamilyProvider.tsx @@ -46,11 +46,21 @@ export function FamilyProvider({ children: providerChildren }: { children: React useEffect(() => { async function fetchFamilyData() { try { - // Get family_id from localStorage (set during login) - const storedFamilyId = localStorage.getItem("family_id"); - const familyIdToUse = storedFamilyId || "default"; + // Get current session from database + const sessionRes = await fetch("/api/auth/signin"); + const sessionData = await sessionRes.json(); - const res = await fetch(`/api/children?familyId=${familyIdToUse}`); + if (!sessionData.authenticated) { + // Not logged in, use default + setFamilyId("default"); + setLoading(false); + return; + } + + const sessionFamilyId = sessionData.familyId || "default"; + + // Fetch children for this family + const res = await fetch(`/api/children?familyId=${sessionFamilyId}`); const data = await res.json(); if (data.children?.length > 0) { @@ -66,16 +76,9 @@ export function FamilyProvider({ children: providerChildren }: { children: React setChildId(childList[0].id); } - setFamilyId(familyIdToUse); - - // Get tier and limits from family - const familyRes = await fetch(`/api/family?familyId=${familyIdToUse}`); - const familyData = await familyRes.json(); - - if (familyData.family) { - setTier(familyData.family.tier || "free"); - setMemberCount(familyData.family.max_members || 2); - } + setFamilyId(sessionFamilyId); + setTier(sessionData.tier || "free"); + setMemberCount(2); } catch (err) { console.error("Failed to fetch family:", err); } finally { @@ -86,10 +89,6 @@ export function FamilyProvider({ children: providerChildren }: { children: React fetchFamilyData(); }, []); - // Check if can add more children/members based on tier limits - const canAddChild = () => memberCount < 2 || tier === "pro"; // free = 1 child, pro = unlimited - const canAddMember = () => memberCount < 2 && tier === "free" ? false : true; - return ( NOW() + `; + + if (!sessions || sessions.length === 0) { + return NextResponse.json({ authenticated: false }); + } + + const session = sessions[0]; + + // Get family via family_members + const members = await sql` + SELECT fm.family_id, f.name as family_name, f.tier + FROM family_members fm + JOIN families f ON f.id = fm.family_id + WHERE fm.user_id = ${session.user_id} + `; + + return NextResponse.json({ + authenticated: true, + userId: session.user_id, + email: session.email, + familyId: members[0]?.family_id, + familyName: members[0]?.family_name, + tier: members[0]?.tier, + }); + } catch (error) { + return NextResponse.json({ authenticated: false }); + } } \ No newline at end of file diff --git a/src/app/api/auth/signout/route.ts b/src/app/api/auth/signout/route.ts new file mode 100644 index 0000000..2186312 --- /dev/null +++ b/src/app/api/auth/signout/route.ts @@ -0,0 +1,31 @@ +import { NextResponse } from "next/server"; +import { sql } from "@/db"; +import { cookies } from "next/headers"; + +export async function POST() { + try { + const cookieStore = await cookies(); + const sessionToken = cookieStore.get("session")?.value; + + if (sessionToken) { + // Delete session from database + await sql` + DELETE FROM sessions WHERE session_token = ${sessionToken} + `; + } + + // Clear cookie + const response = NextResponse.json({ success: true }); + response.cookies.set("session", "", { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + maxAge: 0, + path: "/", + }); + + return response; + } catch (error) { + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 962f78a..a6d4b8a 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -1,45 +1,43 @@ "use client"; -import { useState } from "react"; +import { useEffect } from "react"; import { useRouter } from "next/navigation"; export default function LoginPage() { - const [email, setEmail] = useState(""); - const [loading, setLoading] = useState(false); const router = useRouter(); + useEffect(() => { + // Check if already logged in via session cookie + async function checkSession() { + const res = await fetch("/api/auth/signin"); + const data = await res.json(); + if (data.authenticated) { + router.push("/"); + } + } + checkSession(); + }, [router]); + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - setLoading(true); + const form = e.target as HTMLFormElement; + const email = (form.elements.namedItem("email") as HTMLInputElement)?.value; - try { - const res = await fetch("/api/auth/signin", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ email }), - }); + if (!email) return; - const data = await res.json(); + const res = await fetch("/api/auth/signin", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email }), + }); - if (data.success) { - // Store user and family info - localStorage.setItem("user_id", data.userId); - localStorage.setItem("user_email", data.email); - if (data.familyId) { - localStorage.setItem("family_id", data.familyId); - } - if (data.family) { - localStorage.setItem("family", JSON.stringify(data.family)); - } - router.push("/"); - } else { - alert(data.error || "Sign in failed"); - } - } catch (err) { - console.error(err); + const data = await res.json(); + + if (data.success) { + router.push("/"); + } else { + alert(data.error || "Sign in failed"); } - - setLoading(false); }; return ( @@ -50,19 +48,17 @@ export default function LoginPage() {
setEmail(e.target.value)} className="w-full p-4 border rounded-2xl bg-white shadow-sm" required />