From 8ebc8dec5c1d835248ab5ae42240a34838dc5a83 Mon Sep 17 00:00:00 2001 From: admbrainsteel Date: Fri, 30 Oct 2026 13:51:00 -0300 Subject: [PATCH] fix(auth): harden profile sync and fallback on non-uuid id; restore kong gateway --- src/hooks/useAuth.tsx | 27 ++++++-- src/hooks/useUserFunction.tsx | 17 +++-- .../useUserPermissions/useUserPermissions.tsx | 68 ++++++++++++++----- src/hooks/useUserProfile.tsx | 17 +++-- 4 files changed, 98 insertions(+), 31 deletions(-) diff --git a/src/hooks/useAuth.tsx b/src/hooks/useAuth.tsx index 8a8d1f5..8d49fea 100644 --- a/src/hooks/useAuth.tsx +++ b/src/hooks/useAuth.tsx @@ -24,16 +24,33 @@ import { // Sincroniza user Logto com profiles Supabase e retorna o UUID real do Supabase async function syncUserToProfile(user: LogtoUser): Promise { if (!user?.sub) return user.sub; - const email = user.email || `${user.username || 'user'}@logto.local`; + const email = (user.email || `${user.username || 'user'}@logto.local`).trim(); try { - // 1. Tenta buscar o perfil existente pelo email - const { data: existing } = await supabase + // 1. Tenta buscar o perfil existente pelo email (case-insensitive) + let { data: existing, error: selectError } = await supabase .from('profiles') .select('id') - .eq('email', email) + .ilike('email', email) .maybeSingle(); + // Em caso de falha transitória de rede, faz 1 retry rápido antes de desistir + if (selectError) { + await new Promise((r) => setTimeout(r, 800)); + const retry = await supabase + .from('profiles') + .select('id') + .ilike('email', email) + .maybeSingle(); + existing = retry.data; + selectError = retry.error; + } + + if (selectError) { + console.warn('Erro de rede ao consultar profile no Supabase:', selectError); + return user.sub; + } + let profileId = existing?.id; // 2. Se existe, atualizamos o nome e retornamos o UUID @@ -47,7 +64,7 @@ async function syncUserToProfile(user: LogtoUser): Promise { return profileId; } - // 3. Se não existir, criamos um novo com um UUID gerado + // 3. Se confirmadamente não existir, criamos um novo com um UUID gerado profileId = crypto.randomUUID(); const { error: insertError } = await supabase.from('profiles').insert({ id: profileId, diff --git a/src/hooks/useUserFunction.tsx b/src/hooks/useUserFunction.tsx index 9f90f5f..0782234 100644 --- a/src/hooks/useUserFunction.tsx +++ b/src/hooks/useUserFunction.tsx @@ -11,7 +11,8 @@ export const useUserFunction = () => { queryFn: async () => { if (!user?.id) return null; - const { data, error } = await supabase + const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id || ''); + let query = supabase .from('profiles') .select(` function_id, @@ -19,9 +20,17 @@ export const useUserFunction = () => { name, description ) - `) - .eq('id', user.id) - .single(); + `); + + if (isValidUuid) { + query = query.eq('id', user.id); + } else if (user.email) { + query = query.ilike('email', user.email.trim()); + } else { + query = query.eq('id', user.id); + } + + const { data, error } = await query.maybeSingle(); if (error) { console.error('Erro ao buscar função do usuário:', error); diff --git a/src/hooks/useUserPermissions/useUserPermissions.tsx b/src/hooks/useUserPermissions/useUserPermissions.tsx index 9b3d01f..be3a477 100644 --- a/src/hooks/useUserPermissions/useUserPermissions.tsx +++ b/src/hooks/useUserPermissions/useUserPermissions.tsx @@ -24,26 +24,56 @@ export function useUserPermissions() { // Load user permissions const loadUserPermissions = useCallback(async () => { - if (!user?.id) { + if (!user?.id && !user?.email) { setLoading(false); return; } try { console.log(`🔄 Loading permissions for user: ${user.email}`); + const isUuid = Boolean(user.id && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id)); - // Load functional permissions from user privilege - const { data: profile } = await supabase + // Load functional permissions from user privilege (by UUID or fallback by email) + let profileQuery = supabase .from('profiles') .select(` + id, privilege_id, privileges!inner ( name, permissions ) - `) - .eq('id', user.id) - .single(); + `); + + if (isUuid) { + profileQuery = profileQuery.eq('id', user.id); + } else if (user.email) { + profileQuery = profileQuery.ilike('email', user.email.trim()); + } else { + setLoading(false); + return; + } + + let { data: profile } = await profileQuery.maybeSingle(); + + // Se buscou por UUID e não achou, tenta fallback por email + if (!profile && isUuid && user.email) { + const retryByEmail = await supabase + .from('profiles') + .select(` + id, + privilege_id, + privileges!inner ( + name, + permissions + ) + `) + .ilike('email', user.email.trim()) + .maybeSingle(); + profile = retryByEmail.data; + } + + const resolvedUserId = profile?.id || (isUuid ? user.id : null); if (profile?.privileges?.permissions) { // Safe type conversion with validation @@ -61,18 +91,20 @@ export function useUserPermissions() { } // Load specific resource permissions - const { data: resourcePerms } = await supabase - .from('user_interface_permissions') - .select('resource_key, permission') - .eq('user_id', user.id); + if (resolvedUserId) { + const { data: resourcePerms } = await supabase + .from('user_interface_permissions') + .select('resource_key, permission') + .eq('user_id', resolvedUserId); - if (resourcePerms) { - const permsMap = resourcePerms.reduce((acc, perm) => { - acc[perm.resource_key] = perm.permission as PermissionLevel; - return acc; - }, {} as Record); - setResourcePermissions(permsMap); - console.log('✅ Resource permissions loaded:', permsMap); + if (resourcePerms) { + const permsMap = resourcePerms.reduce((acc, perm) => { + acc[perm.resource_key] = perm.permission as PermissionLevel; + return acc; + }, {} as Record); + setResourcePermissions(permsMap); + console.log('✅ Resource permissions loaded:', permsMap); + } } } catch (error) { @@ -80,7 +112,7 @@ export function useUserPermissions() { } finally { setLoading(false); } - }, [user?.id]); + }, [user?.id, user?.email]); useEffect(() => { loadUserPermissions(); diff --git a/src/hooks/useUserProfile.tsx b/src/hooks/useUserProfile.tsx index 2389337..e4fa9bb 100644 --- a/src/hooks/useUserProfile.tsx +++ b/src/hooks/useUserProfile.tsx @@ -30,11 +30,20 @@ export function useUserProfile() { setLoading(true); try { - const { data, error } = await supabase + const isValidUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(user.id || ''); + let query = supabase .from('profiles') - .select('full_name, email, profile_image_url, status') - .eq('id', user.id) - .maybeSingle(); + .select('full_name, email, profile_image_url, status'); + + if (isValidUuid) { + query = query.eq('id', user.id); + } else if (user.email) { + query = query.ilike('email', user.email.trim()); + } else { + query = query.eq('id', user.id); + } + + const { data, error } = await query.maybeSingle(); if (!error && data) { setProfile(data);