From a994a70d903d3e072accfa2690c30dff054951da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gustavo?= <166467972+JoaoGustavo-dev@users.noreply.github.com> Date: Mon, 6 Oct 2025 23:24:57 -0300 Subject: [PATCH] atualizing-api.ts --- susconecta/lib/api.ts | 467 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 466 insertions(+), 1 deletion(-) diff --git a/susconecta/lib/api.ts b/susconecta/lib/api.ts index c009b0f..bfb5d42 100644 --- a/susconecta/lib/api.ts +++ b/susconecta/lib/api.ts @@ -1,5 +1,8 @@ // lib/api.ts +import { ENV_CONFIG } from '@/lib/env-config'; +import { API_KEY } from '@/lib/config'; + export type ApiOk = { success?: boolean; data: T; @@ -194,7 +197,32 @@ async function parse(res: Response): Promise { console.error("[API ERROR]", res.url, res.status, json); const code = (json && (json.error?.code || json.code)) ?? res.status; const msg = (json && (json.error?.message || json.message)) ?? res.statusText; - throw new Error(`${code}: ${msg}`); + + // Mensagens amigáveis para erros comuns + let friendlyMessage = `${code}: ${msg}`; + + // Erro de CPF duplicado + if (code === '23505' && msg.includes('patients_cpf_key')) { + friendlyMessage = 'Já existe um paciente cadastrado com este CPF. Por favor, verifique se o paciente já está registrado no sistema ou use um CPF diferente.'; + } + // Erro de email duplicado (paciente) + else if (code === '23505' && msg.includes('patients_email_key')) { + friendlyMessage = 'Já existe um paciente cadastrado com este email. Por favor, use um email diferente.'; + } + // Erro de CRM duplicado (médico) + else if (code === '23505' && msg.includes('doctors_crm')) { + friendlyMessage = 'Já existe um médico cadastrado com este CRM. Por favor, verifique se o médico já está registrado no sistema.'; + } + // Erro de email duplicado (médico) + else if (code === '23505' && msg.includes('doctors_email_key')) { + friendlyMessage = 'Já existe um médico cadastrado com este email. Por favor, use um email diferente.'; + } + // Outros erros de constraint unique + else if (code === '23505') { + friendlyMessage = 'Registro duplicado: já existe um cadastro com essas informações no sistema.'; + } + + throw new Error(friendlyMessage); } return (json?.data ?? json) as T; @@ -552,6 +580,432 @@ export async function excluirMedico(id: string | number): Promise { await parse(res); } +// ===== USUÁRIOS ===== +export type UserRole = { + id: string; + user_id: string; + role: string; + created_at: string; +}; + +export async function listarUserRoles(): Promise { + const url = `https://mock.apidog.com/m1/1053378-0-default/rest/v1/user_roles`; + const res = await fetch(url, { + method: "GET", + headers: baseHeaders(), + }); + return await parse(res); +} + +export type User = { + id: string; + email: string; + email_confirmed_at: string; + created_at: string; + last_sign_in_at: string; +}; + +export type CurrentUser = { + id: string; + email: string; + email_confirmed_at: string; + created_at: string; + last_sign_in_at: string; +}; + +export type Profile = { + id: string; + full_name: string; + email: string; + phone: string; + avatar_url: string; + disabled: boolean; + created_at: string; + updated_at: string; +}; + +export type ProfileInput = Partial>; + +export type Permissions = { + isAdmin: boolean; + isManager: boolean; + isDoctor: boolean; + isSecretary: boolean; + isAdminOrManager: boolean; +}; + +export type UserInfo = { + user: User; + profile: Profile; + roles: string[]; + permissions: Permissions; +}; + +export async function getCurrentUser(): Promise { + const url = `https://mock.apidog.com/m1/1053378-0-default/auth/v1/user`; + const res = await fetch(url, { + method: "GET", + headers: baseHeaders(), + }); + return await parse(res); +} + +export async function getUserInfo(): Promise { + const url = `https://mock.apidog.com/m1/1053378-0-default/functions/v1/user-info`; + const res = await fetch(url, { + method: "GET", + headers: baseHeaders(), + }); + return await parse(res); +} + +export type CreateUserInput = { + email: string; + full_name: string; + phone: string; + role: string; + password?: string; +}; + +export type CreatedUser = { + id: string; + email: string; + full_name: string; + phone: string; + role: string; +}; + +export type CreateUserResponse = { + success: boolean; + user: CreatedUser; + password?: string; +}; + +export type CreateUserWithPasswordResponse = { + success: boolean; + user: CreatedUser; + email: string; + password: string; +}; + +// Função para gerar senha aleatória (formato: senhaXXX!) +export function gerarSenhaAleatoria(): string { + const num1 = Math.floor(Math.random() * 10); + const num2 = Math.floor(Math.random() * 10); + const num3 = Math.floor(Math.random() * 10); + return `senha${num1}${num2}${num3}!`; +} + +export async function criarUsuario(input: CreateUserInput): Promise { + const url = `https://mock.apidog.com/m1/1053378-0-default/functions/v1/create-user`; + const res = await fetch(url, { + method: "POST", + headers: { ...baseHeaders(), "Content-Type": "application/json" }, + body: JSON.stringify(input), + }); + return await parse(res); +} + +// ============================================ +// CRIAÇÃO DE USUÁRIOS NO SUPABASE AUTH +// Vínculo com pacientes/médicos por EMAIL +// ============================================ + +// Criar usuário para MÉDICO no Supabase Auth (sistema de autenticação) +export async function criarUsuarioMedico(medico: { + email: string; + full_name: string; + phone_mobile: string; +}): Promise { + + const senha = gerarSenhaAleatoria(); + + console.log('🏥 [CRIAR MÉDICO] Iniciando criação no Supabase Auth...'); + console.log('📧 Email:', medico.email); + console.log('👤 Nome:', medico.full_name); + console.log('📱 Telefone:', medico.phone_mobile); + console.log('🔑 Senha gerada:', senha); + + // Endpoint do Supabase Auth (mesmo que auth.ts usa) + const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`; + + const payload = { + email: medico.email, + password: senha, + data: { + userType: 'profissional', // Para login em /login -> /profissional + full_name: medico.full_name, + phone: medico.phone_mobile, + } + }; + + console.log('📤 [CRIAR MÉDICO] Enviando para:', signupUrl); + + try { + const response = await fetch(signupUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + "apikey": API_KEY, + }, + body: JSON.stringify(payload), + }); + + console.log('📋 [CRIAR MÉDICO] Status da resposta:', response.status, response.statusText); + + if (!response.ok) { + const errorText = await response.text(); + console.error('❌ [CRIAR MÉDICO] Erro na resposta:', errorText); + + // Tenta parsear o erro para pegar mensagem específica + let errorMsg = `Erro ao criar usuário (${response.status})`; + try { + const errorData = JSON.parse(errorText); + errorMsg = errorData.msg || errorData.message || errorData.error_description || errorMsg; + + // Mensagens amigáveis para erros comuns + if (errorMsg.includes('already registered') || errorMsg.includes('already exists')) { + errorMsg = 'Este email já está cadastrado no sistema'; + } else if (errorMsg.includes('invalid email')) { + errorMsg = 'Formato de email inválido'; + } else if (errorMsg.includes('weak password')) { + errorMsg = 'Senha muito fraca'; + } + } catch (e) { + // Se não conseguir parsear, usa mensagem genérica + } + + throw new Error(errorMsg); + } + + const responseData = await response.json(); + console.log('✅ [CRIAR MÉDICO] Usuário criado com sucesso no Supabase Auth!'); + console.log('🆔 User ID:', responseData.user?.id || responseData.id); + + // 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário + // Isso força o Supabase a confirmar o email automaticamente + if (responseData.user?.email_confirmed_at === null || !responseData.user?.email_confirmed_at) { + console.warn('⚠️ [CRIAR MÉDICO] Email NÃO confirmado - tentando auto-confirmar via login...'); + + try { + const loginUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/token?grant_type=password`; + console.log('🔧 [AUTO-CONFIRMAR] Fazendo login automático para confirmar email...'); + + const loginResponse = await fetch(loginUrl, { + method: 'POST', + headers: { + 'apikey': ENV_CONFIG.SUPABASE_ANON_KEY, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: medico.email, + password: senha, + }), + }); + + if (loginResponse.ok) { + const loginData = await loginResponse.json(); + console.log('✅ [AUTO-CONFIRMAR] Login automático realizado com sucesso!'); + console.log('📦 [AUTO-CONFIRMAR] Email confirmado:', loginData.user?.email_confirmed_at ? 'SIM ✅' : 'NÃO ❌'); + + // Atualizar responseData com dados do login (que tem email confirmado) + if (loginData.user) { + responseData.user = loginData.user; + } + } else { + const errorText = await loginResponse.text(); + console.error('❌ [AUTO-CONFIRMAR] Falha no login automático:', loginResponse.status, errorText); + console.warn('⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!'); + } + } catch (confirmError) { + console.error('❌ [AUTO-CONFIRMAR] Erro ao tentar fazer login automático:', confirmError); + console.warn('⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...'); + } + } else { + console.log('✅ [CRIAR MÉDICO] Email confirmado automaticamente!'); + } + + // Log bem visível com as credenciais para teste + console.log('🔐🔐🔐 ========================================'); + console.log('🔐 CREDENCIAIS DO MÉDICO CRIADO:'); + console.log('🔐 Email:', medico.email); + console.log('🔐 Senha:', senha); + console.log('🔐 Pode fazer login?', responseData.user?.email_confirmed_at ? 'SIM ✅' : 'NÃO ❌ (precisa confirmar email)'); + console.log('🔐 ========================================'); + + return { + success: true, + user: responseData.user || responseData, + email: medico.email, + password: senha, + }; + + } catch (error: any) { + console.error('❌ [CRIAR MÉDICO] Erro ao criar usuário:', error); + throw error; + } +} + +// Criar usuário para PACIENTE no Supabase Auth (sistema de autenticação) +export async function criarUsuarioPaciente(paciente: { + email: string; + full_name: string; + phone_mobile: string; +}): Promise { + + const senha = gerarSenhaAleatoria(); + + console.log('🏥 [CRIAR PACIENTE] Iniciando criação no Supabase Auth...'); + console.log('📧 Email:', paciente.email); + console.log('👤 Nome:', paciente.full_name); + console.log('📱 Telefone:', paciente.phone_mobile); + console.log('🔑 Senha gerada:', senha); + + // Endpoint do Supabase Auth (mesmo que auth.ts usa) + const signupUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signup`; + + const payload = { + email: paciente.email, + password: senha, + data: { + userType: 'paciente', // Para login em /login-paciente -> /paciente + full_name: paciente.full_name, + phone: paciente.phone_mobile, + } + }; + + console.log('📤 [CRIAR PACIENTE] Enviando para:', signupUrl); + + try { + const response = await fetch(signupUrl, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Accept": "application/json", + "apikey": API_KEY, + }, + body: JSON.stringify(payload), + }); + + console.log('📋 [CRIAR PACIENTE] Status da resposta:', response.status, response.statusText); + + if (!response.ok) { + const errorText = await response.text(); + console.error('❌ [CRIAR PACIENTE] Erro na resposta:', errorText); + + // Tenta parsear o erro para pegar mensagem específica + let errorMsg = `Erro ao criar usuário (${response.status})`; + try { + const errorData = JSON.parse(errorText); + errorMsg = errorData.msg || errorData.message || errorData.error_description || errorMsg; + + // Mensagens amigáveis para erros comuns + if (errorMsg.includes('already registered') || errorMsg.includes('already exists')) { + errorMsg = 'Este email já está cadastrado no sistema'; + } else if (errorMsg.includes('invalid email')) { + errorMsg = 'Formato de email inválido'; + } else if (errorMsg.includes('weak password')) { + errorMsg = 'Senha muito fraca'; + } + } catch (e) { + // Se não conseguir parsear, usa mensagem genérica + } + + throw new Error(errorMsg); + } + + const responseData = await response.json(); + console.log('✅ [CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!'); + console.log('🆔 User ID:', responseData.user?.id || responseData.id); + console.log('📦 [CRIAR PACIENTE] Resposta completa do Supabase:', JSON.stringify(responseData, null, 2)); + + // VERIFICAÇÃO CRÍTICA: O usuário foi realmente criado? + if (!responseData.user && !responseData.id) { + console.error('⚠️⚠️⚠️ AVISO: Supabase retornou sucesso mas SEM user ID!'); + console.error('Isso pode significar que o usuário NÃO foi criado de verdade!'); + } + + const userId = responseData.user?.id || responseData.id; + + // 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário + // Isso força o Supabase a confirmar o email automaticamente + if (responseData.user?.email_confirmed_at === null || !responseData.user?.email_confirmed_at) { + console.warn('⚠️ [CRIAR PACIENTE] Email NÃO confirmado - tentando auto-confirmar via login...'); + + try { + const loginUrl = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/token?grant_type=password`; + console.log('🔧 [AUTO-CONFIRMAR] Fazendo login automático para confirmar email...'); + + const loginResponse = await fetch(loginUrl, { + method: 'POST', + headers: { + 'apikey': ENV_CONFIG.SUPABASE_ANON_KEY, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email: paciente.email, + password: senha, + }), + }); + + console.log('🔍 [AUTO-CONFIRMAR] Status do login automático:', loginResponse.status); + + if (loginResponse.ok) { + const loginData = await loginResponse.json(); + console.log('✅ [AUTO-CONFIRMAR] Login automático realizado com sucesso!'); + console.log('📦 [AUTO-CONFIRMAR] Dados completos do login:', JSON.stringify(loginData, undefined, 2)); + console.log('📧 [AUTO-CONFIRMAR] Email confirmado:', loginData.user?.email_confirmed_at ? 'SIM ✅' : 'NÃO ❌'); + console.log('👤 [AUTO-CONFIRMAR] UserType no metadata:', loginData.user?.user_metadata?.userType); + console.log('🎯 [AUTO-CONFIRMAR] Email verified:', loginData.user?.user_metadata?.email_verified); + + // Atualizar responseData com dados do login (que tem email confirmado) + if (loginData.user) { + responseData.user = loginData.user; + } + } else { + const errorText = await loginResponse.text(); + console.error('❌ [AUTO-CONFIRMAR] Falha no login automático:', loginResponse.status, errorText); + console.warn('⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!'); + + // Tentar parsear o erro para entender melhor + try { + const errorData = JSON.parse(errorText); + console.error('📋 [AUTO-CONFIRMAR] Detalhes do erro:', errorData); + } catch (e) { + console.error('📋 [AUTO-CONFIRMAR] Erro não é JSON:', errorText); + } + } + } catch (confirmError) { + console.error('❌ [AUTO-CONFIRMAR] Erro ao tentar fazer login automático:', confirmError); + console.warn('⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...'); + } + } else { + console.log('✅ [CRIAR PACIENTE] Email confirmado automaticamente!'); + } + + // Log bem visível com as credenciais para teste + console.log('🔐🔐🔐 ========================================'); + console.log('🔐 CREDENCIAIS DO PACIENTE CRIADO:'); + console.log('🔐 Email:', paciente.email); + console.log('🔐 Senha:', senha); + console.log('🔐 UserType:', 'paciente'); + console.log('🔐 Pode fazer login?', responseData.user?.email_confirmed_at ? 'SIM ✅' : 'NÃO ❌ (precisa confirmar email)'); + console.log('🔐 ========================================'); + + return { + success: true, + user: responseData.user || responseData, + email: paciente.email, + password: senha, + }; + + } catch (error: any) { + console.error('❌ [CRIAR PACIENTE] Erro ao criar usuário:', error); + throw error; + } +} + // ===== CEP (usado nos formulários) ===== export async function buscarCepAPI(cep: string): Promise<{ logradouro?: string; @@ -588,3 +1042,14 @@ export async function adicionarAnexoMedico(_id: string | number, _file: File): P export async function removerAnexoMedico(_id: string | number, _anexoId: string | number): Promise {} export async function uploadFotoMedico(_id: string | number, _file: File): Promise<{ foto_url?: string; thumbnail_url?: string }> { return {}; } export async function removerFotoMedico(_id: string | number): Promise {} + +// ===== PERFIS DE USUÁRIOS ===== +export async function listarPerfis(): Promise { + const url = `https://mock.apidog.com/m1/1053378-0-default/rest/v1/profiles`; + const res = await fetch(url, { + method: "GET", + headers: baseHeaders(), + }); + return await parse(res); +} +