From 75baf2be114c2006614808f65bdb6280bde90f3f Mon Sep 17 00:00:00 2001 From: Emiya Kiritsugu Date: Wed, 8 Jul 2026 10:18:58 -0300 Subject: [PATCH] feat(aluno): auto-provision perfil on Google OAuth login MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Google OAuth users had no aluno row, landing on 'perfil não encontrado' empty-state. Lazy-provision on first /aluno/dashboard visit: - findUnique miss -> prisma.aluno.create with email + user_metadata (full_name, avatar_url) + deterministic CPF placeholder OAUTH- - race guarded: concurrent-create catch refetches - same pattern applies to GitHub/Apple OAuth providers Also fix site_url fallback (was localhost, sent OAuth mismatches to localhost root with ?code=): set to production domain. Local dev still works via additional_redirect_urls localhost entry. Co-Authored-By: Claude --- src/app/aluno/dashboard/page.tsx | 69 +++++++++++++++++++++++++++++++- supabase/config.toml | 7 +++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/app/aluno/dashboard/page.tsx b/src/app/aluno/dashboard/page.tsx index d9a1b6e4..bdfe785d 100644 --- a/src/app/aluno/dashboard/page.tsx +++ b/src/app/aluno/dashboard/page.tsx @@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() { }, }); - const aluno = await alunoPromise; + let aluno = await alunoPromise; + + // Auto-provision: OAuth users (Google/GitHub/Apple) who have no aluno row get + // one created lazily on first dashboard visit. Email+name come from Supabase + // auth.user_metadata; CPF is required @unique but unknown from Google, so we + // mint a deterministic placeholder from the auth user id (uniqueness from the + // already-unique auth uid). Admin can fix the real CPF later. Throws on the + // rare race where two concurrent requests both miss findUnique — caught below + // surfaces as the original empty-state so the next request finds the row. + if (!aluno) { + const meta = user.user_metadata ?? {}; + const nomeCompleto = + (meta.full_name as string | undefined) ?? + (meta.name as string | undefined) ?? + user.email ?? + 'Aluno Google'; + const fotoUrl = (meta.avatar_url as string | undefined) ?? null; + const cpfPlaceholder = `OAUTH-${user.id.slice(0, 18)}`; + try { + aluno = await prisma.aluno.create({ + data: { + email: user.email!, + nomeCompleto, + fotoUrl, + cpf: cpfPlaceholder, + }, + select: { + id: true, + nomeCompleto: true, + fotoUrl: true, + nivel: true, + exp: true, + statusMatricula: true, + streakDiasSeguidos: true, + treinosNoMes: true, + ultimoTreinoData: true, + Matriculas: { + where: { status: 'ATIVA' }, + orderBy: { dataVencimento: 'desc' }, + take: 1, + select: { id: true, status: true, dataVencimento: true, planoId: true }, + }, + }, + }); + } catch { + // Race: another request created the row. Refetch instead of crashing. + aluno = await prisma.aluno.findUnique({ + where: { email: user.email }, + select: { + id: true, + nomeCompleto: true, + fotoUrl: true, + nivel: true, + exp: true, + statusMatricula: true, + streakDiasSeguidos: true, + treinosNoMes: true, + ultimoTreinoData: true, + Matriculas: { + where: { status: 'ATIVA' }, + orderBy: { dataVencimento: 'desc' }, + take: 1, + select: { id: true, status: true, dataVencimento: true, planoId: true }, + }, + }, + }); + } + } if (!aluno) { return ( diff --git a/supabase/config.toml b/supabase/config.toml index d5be74f5..0df62d5f 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -150,8 +150,11 @@ max_indexes = 5 [auth] enabled = true # The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://localhost:3000" +# in emails. Must be the production URL — Supabase falls back to this when a `redirectTo` +# fails allowlist validation, so leaving it as localhost in a linked cloud project sends +# users to localhost on every OAuth redirect mismatch. Local dev resolves via +# additional_redirect_urls (localhost entry) instead. +site_url = "https://smartmanagementsystem.vercel.app" # A list of *exact* URLs that auth providers are permitted to redirect to post authentication. additional_redirect_urls = [ "http://localhost:3000/auth/callback",