feat(aluno): auto-provision perfil on Google OAuth login#197
feat(aluno): auto-provision perfil on Google OAuth login#197EmiyaKiritsugu3 wants to merge 1 commit into
Conversation
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-<uid14> - 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 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe dashboard page now auto-provisions a Prisma ChangesAluno Dashboard Auto-Provisioning
Supabase Auth Configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: one or more packages not found in the registry. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
src/app/aluno/dashboard/page.tsx (1)
63-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the duplicated
selectshape to avoid drift.The identical
selectblock is now repeated three times (findUnique at Lines 18-34, create at Lines 63-79, refetch at Lines 85-101). These must stay byte-for-byte in sync or the dashboard will receive inconsistently shaped data depending on which path ran. Hoist it into a singleconst(e.g.dashboardAlunoSelect) and reuse it in all three queries.♻️ Sketch
const dashboardAlunoSelect = { 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 }, }, } satisfies Prisma.AlunoSelect;Then pass
select: dashboardAlunoSelectto each query.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/aluno/dashboard/page.tsx` around lines 63 - 101, The dashboard aluno queries are repeating the same Prisma select shape in multiple places, which risks drift between the initial lookup, create path, and refetch path. Hoist the shared select into a single reusable constant (for example, a dashboardAlunoSelect in page.tsx) and reuse it in the prisma.aluno.findUnique and create/refetch calls so all branches return the exact same fields, including Matriculas.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/aluno/dashboard/page.tsx`:
- Around line 81-103: The `catch` in `dashboard/page.tsx` is too broad and
currently treats every `prisma.aluno.create` failure as a concurrent insert.
Narrow the handler around the `aluno` creation path to only recover from the
unique-constraint race (check for Prisma `P2002`), then keep the existing
`findUnique` refetch; for any other error, log it and rethrow so real
DB/validation failures are not swallowed. Use the `prisma.aluno.create` and
fallback `prisma.aluno.findUnique` block as the fix point.
- Line 58: The dashboard flow currently mixes `user.email` and `user.email!`,
which leaves the no-email OAuth path inconsistent. In `page.tsx`, handle the
email once at the start of the component or handler by reading `user.email` into
a local `email` variable, then guard or redirect early if it is missing before
any Prisma queries. After that, reuse the same `email` variable throughout the
rest of the logic instead of accessing `user.email` again.
---
Nitpick comments:
In `@src/app/aluno/dashboard/page.tsx`:
- Around line 63-101: The dashboard aluno queries are repeating the same Prisma
select shape in multiple places, which risks drift between the initial lookup,
create path, and refetch path. Hoist the shared select into a single reusable
constant (for example, a dashboardAlunoSelect in page.tsx) and reuse it in the
prisma.aluno.findUnique and create/refetch calls so all branches return the
exact same fields, including Matriculas.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 51998a22-d27f-4360-b2a8-438729782170
📒 Files selected for processing (2)
src/app/aluno/dashboard/page.tsxsupabase/config.toml
| try { | ||
| aluno = await prisma.aluno.create({ | ||
| data: { | ||
| email: user.email!, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect getUser return type and the User.email typing to confirm nullability.
fd -t f 'server.(ts|tsx)' src/utils/supabase --exec cat -n {}
rg -nP '\buser\.email\b' src/app/aluno/dashboard/page.tsxRepository: EmiyaKiritsugu3/PWeb_Project
Length of output: 2170
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the dashboard page around the cited lines and the getUser helper.
file="src/app/aluno/dashboard/page.tsx"
echo "== file size =="
wc -l "$file"
echo "== relevant slices =="
sed -n '1,180p' "$file"
echo "== search for guards / redirects related to email =="
rg -n "email|redirect|notFound|throw|return null|optional|undefined" "$file"Repository: EmiyaKiritsugu3/PWeb_Project
Length of output: 5913
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="src/app/aluno/dashboard/page.tsx"
# Show tighter context around the reported lines.
sed -n '1,140p' "$file" | nl -ba | sed -n '1,140p'
# Also inspect the Supabase auth user type if it is vendored in the repo lock/types.
rg -n "type User|interface User|email\?:|email:.*undefined" . -g '!node_modules' -g '!dist' -g '!build'Repository: EmiyaKiritsugu3/PWeb_Project
Length of output: 206
Handle user.email once up front. If the email can be missing, guard or redirect before the Prisma queries and reuse a local email value; mixing user.email and user.email! leaves the no-email OAuth path inconsistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/aluno/dashboard/page.tsx` at line 58, The dashboard flow currently
mixes `user.email` and `user.email!`, which leaves the no-email OAuth path
inconsistent. In `page.tsx`, handle the email once at the start of the component
or handler by reading `user.email` into a local `email` variable, then guard or
redirect early if it is missing before any Prisma queries. After that, reuse the
same `email` variable throughout the rest of the logic instead of accessing
`user.email` again.
| } 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 }, | ||
| }, | ||
| }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file outline ==\n'
ast-grep outline src/app/aluno/dashboard/page.tsx --view expanded
printf '\n== relevant lines 1-170 ==\n'
cat -n src/app/aluno/dashboard/page.tsx | sed -n '1,170p'
printf '\n== prisma/aluno references in file ==\n'
rg -n "prisma\.aluno|Prisma|Sentry|catch" src/app/aluno/dashboard/page.tsx
printf '\n== imports at top ==\n'
sed -n '1,35p' src/app/aluno/dashboard/page.tsxRepository: EmiyaKiritsugu3/PWeb_Project
Length of output: 7943
Narrow the catch to the unique-violation race. A bare catch {} treats every create failure as a concurrent insert, so transient DB/validation errors get swallowed and the user falls back to the empty state. Handle only P2002 here and log/rethrow the rest.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/aluno/dashboard/page.tsx` around lines 81 - 103, The `catch` in
`dashboard/page.tsx` is too broad and currently treats every
`prisma.aluno.create` failure as a concurrent insert. Narrow the handler around
the `aluno` creation path to only recover from the unique-constraint race (check
for Prisma `P2002`), then keep the existing `findUnique` refetch; for any other
error, log it and rethrow so real DB/validation failures are not swallowed. Use
the `prisma.aluno.create` and fallback `prisma.aluno.findUnique` block as the
fix point.
Source: Coding guidelines
There was a problem hiding this comment.
5 issues found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/app/aluno/dashboard/page.tsx">
<violation number="1" location="src/app/aluno/dashboard/page.tsx:46">
P1: Custom agent: **Enforce Pragmatic Test Coverage**
The auto-provision logic is core business logic, but its success and failure paths are not exercised by the current tests. `page.test.tsx` only mocks `prisma.aluno.findUnique` and does not cover the new `prisma.aluno.create` call or the race-condition refetch in the catch block. Consider adding tests that mock a successful creation and simulate the unique-violation race case so both branches are validated.</violation>
<violation number="2" location="src/app/aluno/dashboard/page.tsx:48">
P3: OAuth users with an empty `full_name`/`name` metadata value can be auto-provisioned with a blank display name because `??` does not fall back on empty strings. Treating non-empty trimmed strings as valid before falling back to email keeps the dashboard identity usable.</violation>
<violation number="3" location="src/app/aluno/dashboard/page.tsx:63">
P3: The `select` projection block (15 lines) is duplicated verbatim between the `aluno.create()` call and the catch block's `findUnique()` refetch. If the projection changes — adding a new field, renaming a relation — both copies must be updated in sync. Consider extracting the shared `select` object into a variable or function to keep them consistent.</violation>
<violation number="4" location="src/app/aluno/dashboard/page.tsx:81">
P2: First-time OAuth users can still land on the “perfil de aluno não foi encontrado” fallback for real create failures because this catches every `prisma.aluno.create` error as if it were only a race. Narrowing this to Prisma `P2002` unique-constraint errors and rethrowing/logging other errors would preserve the intended auto-provisioning behavior and make failures visible.</violation>
</file>
<file name="supabase/config.toml">
<violation number="1" location="supabase/config.toml:157">
P2: Local password signup/email confirmation will now send developers to the production app when using the local Supabase stack. The `signUp` flow does not provide `emailRedirectTo`, so this production `site_url` becomes the fallback for confirmation links; consider adding an explicit local/prod callback URL in that signup call or separating local/cloud auth config.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // 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) { |
There was a problem hiding this comment.
P1: Custom agent: Enforce Pragmatic Test Coverage
The auto-provision logic is core business logic, but its success and failure paths are not exercised by the current tests. page.test.tsx only mocks prisma.aluno.findUnique and does not cover the new prisma.aluno.create call or the race-condition refetch in the catch block. Consider adding tests that mock a successful creation and simulate the unique-violation race case so both branches are validated.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 46:
<comment>The auto-provision logic is core business logic, but its success and failure paths are not exercised by the current tests. `page.test.tsx` only mocks `prisma.aluno.findUnique` and does not cover the new `prisma.aluno.create` call or the race-condition refetch in the catch block. Consider adding tests that mock a successful creation and simulate the unique-violation race case so both branches are validated.</comment>
<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+ // 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 =
</file context>
| }, | ||
| }, | ||
| }); | ||
| } catch { |
There was a problem hiding this comment.
P2: First-time OAuth users can still land on the “perfil de aluno não foi encontrado” fallback for real create failures because this catches every prisma.aluno.create error as if it were only a race. Narrowing this to Prisma P2002 unique-constraint errors and rethrowing/logging other errors would preserve the intended auto-provisioning behavior and make failures visible.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 81:
<comment>First-time OAuth users can still land on the “perfil de aluno não foi encontrado” fallback for real create failures because this catches every `prisma.aluno.create` error as if it were only a race. Narrowing this to Prisma `P2002` unique-constraint errors and rethrowing/logging other errors would preserve the intended auto-provisioning behavior and make failures visible.</comment>
<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+ },
+ },
+ });
+ } catch {
+ // Race: another request created the row. Refetch instead of crashing.
+ aluno = await prisma.aluno.findUnique({
</file context>
| # 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" |
There was a problem hiding this comment.
P2: Local password signup/email confirmation will now send developers to the production app when using the local Supabase stack. The signUp flow does not provide emailRedirectTo, so this production site_url becomes the fallback for confirmation links; consider adding an explicit local/prod callback URL in that signup call or separating local/cloud auth config.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/config.toml, line 157:
<comment>Local password signup/email confirmation will now send developers to the production app when using the local Supabase stack. The `signUp` flow does not provide `emailRedirectTo`, so this production `site_url` becomes the fallback for confirmation links; consider adding an explicit local/prod callback URL in that signup call or separating local/cloud auth config.</comment>
<file context>
@@ -150,8 +150,11 @@ max_indexes = 5
+# 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 = [
</file context>
| const nomeCompleto = | ||
| (meta.full_name as string | undefined) ?? | ||
| (meta.name as string | undefined) ?? | ||
| user.email ?? | ||
| 'Aluno Google'; |
There was a problem hiding this comment.
P3: OAuth users with an empty full_name/name metadata value can be auto-provisioned with a blank display name because ?? does not fall back on empty strings. Treating non-empty trimmed strings as valid before falling back to email keeps the dashboard identity usable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 48:
<comment>OAuth users with an empty `full_name`/`name` metadata value can be auto-provisioned with a blank display name because `??` does not fall back on empty strings. Treating non-empty trimmed strings as valid before falling back to email keeps the dashboard identity usable.</comment>
<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+ // 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) ??
</file context>
| const nomeCompleto = | |
| (meta.full_name as string | undefined) ?? | |
| (meta.name as string | undefined) ?? | |
| user.email ?? | |
| 'Aluno Google'; | |
| const nomeCompleto = | |
| (typeof meta.full_name === 'string' && meta.full_name.trim()) || | |
| (typeof meta.name === 'string' && meta.name.trim()) || | |
| user.email || | |
| 'Aluno Google'; |
| fotoUrl, | ||
| cpf: cpfPlaceholder, | ||
| }, | ||
| select: { |
There was a problem hiding this comment.
P3: The select projection block (15 lines) is duplicated verbatim between the aluno.create() call and the catch block's findUnique() refetch. If the projection changes — adding a new field, renaming a relation — both copies must be updated in sync. Consider extracting the shared select object into a variable or function to keep them consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/app/aluno/dashboard/page.tsx, line 63:
<comment>The `select` projection block (15 lines) is duplicated verbatim between the `aluno.create()` call and the catch block's `findUnique()` refetch. If the projection changes — adding a new field, renaming a relation — both copies must be updated in sync. Consider extracting the shared `select` object into a variable or function to keep them consistent.</comment>
<file context>
@@ -34,7 +34,74 @@ export default async function AlunoDashboardPage() {
+ fotoUrl,
+ cpf: cpfPlaceholder,
+ },
+ select: {
+ id: true,
+ nomeCompleto: true,
</file context>



Problem
Google OAuth login completed (session cookie set, user authenticated) but landed on "Sinto muito! Seu perfil de aluno não foi encontrado" because no
Alunorow existed for the Google email in the Prismaalunostable. Only admin-seeded email/password users had aluno rows.Separately: Supabase
site_urlwashttp://localhost:3000, so whenredirectTofailed allowlist validation the OAuth flow dropped users onhttp://localhost:3000/?code=...→ERR_CONNECTION_REFUSED.Fix
1. Auto-provision aluno on first visit (
src/app/aluno/dashboard/page.tsx)findUnique({ where: { email: user.email } })miss →prisma.aluno.create:emailfrom Supabase auth usernomeCompletofromuser.user_metadata.full_name(Google providesfull_name)fotoUrlfromuser.user_metadata.avatar_urlcpfplaceholderOAUTH-<auth_uid[0:18]>— CPF is@uniquerequired but unknown from Google; deterministic so re-visits don't duplicate. Admin fixes real CPF later.Race-guarded:
createwrapped in try/catch; on unique-violation (concurrent first-visits), refetch instead of crashing. Original empty-state kept as last-resort fallback.Applies identically to GitHub/Apple OAuth (same
user_metadatashape).2. Production
site_url(supabase/config.toml)site_url = "http://localhost:3000"→"https://smartmanagementsystem.vercel.app".site_urlis Supabase's fallback whenredirectTofails allowlist validation — leaving it as localhost sent every mismatch tolocalhostroot. Local dev still works viaadditional_redirect_urlshttp://localhost:3000/auth/callbackentry (Supabase allows redirects to allowlist even when site_url differs).Verification
npx tsc --noEmit— 0 errorsnpx vitest run src/app/aluno/dashboard/page.test.tsx— 6/6 passnpx eslint src/app/aluno/dashboard/page.tsx— no issuessupabase config pushapplied (auth serviceupdated)Test plan
After merge deploy:
/aluno/login→ "Entrar com Google" → pick any Gmail/aluno/dashboard(not empty-state), with nome/avatar from Google profile🤖 Generated with Claude Code
Summary by cubic
Auto-provisions an
Alunoprofile on first OAuth login so users land on the dashboard instead of “perfil não encontrado”. Also setssite_urlinsupabaseconfig to the production domain to prevent OAuth redirects to localhost.New Features
Alunoon first dashboard visit when missing, using email,full_name,avatar_url, and a deterministic CPF placeholder (OAUTH-<uid>). Race-guarded to avoid duplicates. Applies to Google/GitHub/Apple OAuth.Bug Fixes
supabase/config.tomlsite_urltohttps://smartmanagementsystem.vercel.appto stop fallback redirects to localhost. Local dev continues viaadditional_redirect_urls.Written for commit 75baf2b. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes