feat(auth): separate doctor/patient roles and correct mapping
separate flow: doctors → user_roles add list/update user_roles map patient->user, doctor->doctor adjust list/update patient_assignments extend verifyLoginPermission correct 22P02 and FK 23503; basic tests performed
This commit is contained in:
parent
7819eb2fdf
commit
c48efe8336
@ -46,7 +46,7 @@ import {
|
||||
buscarMedicoPorId,
|
||||
Medico,
|
||||
listarAutorizacoesUsuario,
|
||||
atualizarAutorizacoesUsuario,
|
||||
atualizarUserRoles,
|
||||
buscarUsuarioPorEmail,
|
||||
criarUsuarioMedico,
|
||||
type AuthorizationRole,
|
||||
@ -425,12 +425,14 @@ export default function DoutoresPage() {
|
||||
if (selection.paciente) selectedRoles.push("paciente");
|
||||
if (selection.medico) selectedRoles.push("medico");
|
||||
|
||||
console.log("[Auth] Atualizando roles:", selectedRoles, "para user_id:", userId, "doctor_id:", authTargetDoctor.id);
|
||||
const result = await atualizarAutorizacoesUsuario(
|
||||
console.log(
|
||||
"[Auth] Atualizando roles (user_roles) :",
|
||||
selectedRoles,
|
||||
"para user_id:",
|
||||
userId,
|
||||
authTargetDoctor.id, // doctor_id (usamos como patient_id na tabela)
|
||||
selectedRoles
|
||||
);
|
||||
// Para médicos vamos gravar roles globais na tabela `user_roles` (sem patient_id)
|
||||
const result = await atualizarUserRoles(userId, selectedRoles);
|
||||
console.log("[Auth] Resultado:", result);
|
||||
|
||||
toast({
|
||||
|
||||
@ -75,14 +75,6 @@ export function CredentialsDialog({
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert className="bg-blue-50 border-blue-200">
|
||||
<AlertDescription className="text-blue-900">
|
||||
<strong>📧 Confirme o email:</strong> Um email de confirmação foi
|
||||
enviado para <strong>{email}</strong>. O {userType} deve clicar no
|
||||
link de confirmação antes de fazer o primeiro login.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email de Acesso</Label>
|
||||
@ -147,33 +139,6 @@ export function CredentialsDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-md p-3 text-sm text-blue-900">
|
||||
<strong>Próximos passos:</strong>
|
||||
<ol className="list-decimal list-inside mt-2 space-y-1">
|
||||
<li>Compartilhe estas credenciais com o {userType}</li>
|
||||
<li>
|
||||
<strong className="text-blue-700">
|
||||
O {userType} deve confirmar o email
|
||||
</strong>{" "}
|
||||
clicando no link enviado para <strong>{email}</strong> (verifique
|
||||
também a pasta de spam)
|
||||
</li>
|
||||
<li>
|
||||
Após confirmar o email, o {userType} deve acessar:{" "}
|
||||
<code className="bg-blue-100 px-1 py-0.5 rounded text-xs font-mono">
|
||||
{userType === "médico" ? "/login" : "/login-paciente"}
|
||||
</code>
|
||||
</li>
|
||||
<li>
|
||||
Após o login, terá acesso à área:{" "}
|
||||
<code className="bg-blue-100 px-1 py-0.5 rounded text-xs font-mono">
|
||||
{userType === "médico" ? "/profissional" : "/paciente"}
|
||||
</code>
|
||||
</li>
|
||||
<li>Recomende trocar a senha no primeiro acesso</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="flex-col sm:flex-row gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@ -480,8 +480,8 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
|
||||
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
|
||||
const array = await parse<Medico[]>(res);
|
||||
if (array && array.length > 0) {
|
||||
console.log("✅ Médico encontrado no Supabase:", array[0]);
|
||||
console.log("🔍 Campo especialidade no médico:", {
|
||||
console.log("Médico encontrado no Supabase:", array[0]);
|
||||
console.log("Campo especialidade no médico:", {
|
||||
especialidade: array[0].especialidade,
|
||||
specialty: (array[0] as any).specialty,
|
||||
hasEspecialidade: !!array[0].especialidade,
|
||||
@ -490,7 +490,7 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
|
||||
return array[0];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("⚠️ Erro ao buscar no Supabase, tentando mock API:", error);
|
||||
console.warn("Erro ao buscar no Supabase, tentando mock API:", error);
|
||||
}
|
||||
|
||||
// Se não encontrar no Supabase, tenta o mock API
|
||||
@ -511,10 +511,10 @@ export async function buscarMedicoPorId(id: string | number): Promise<Medico> {
|
||||
}
|
||||
|
||||
const medico = await res.json();
|
||||
console.log("✅ Médico encontrado no Mock API:", medico);
|
||||
console.log("Médico encontrado no Mock API:", medico);
|
||||
return medico as Medico;
|
||||
} catch (error) {
|
||||
console.error("❌ Erro ao buscar médico em ambas as APIs:", error);
|
||||
console.error("Erro ao buscar médico em ambas as APIs:", error);
|
||||
throw new Error("404: Médico não encontrado");
|
||||
}
|
||||
}
|
||||
@ -541,8 +541,8 @@ export async function atualizarMedico(
|
||||
id: string | number,
|
||||
input: MedicoInput,
|
||||
): Promise<Medico> {
|
||||
console.log(`🔄 Tentando atualizar médico ID: ${id}`);
|
||||
console.log(`📤 Payload original:`, input);
|
||||
console.log(` Tentando atualizar médico ID: ${id}`);
|
||||
console.log(` Payload original:`, input);
|
||||
|
||||
// Criar um payload limpo apenas com campos básicos que sabemos que existem
|
||||
const cleanPayload = {
|
||||
@ -560,12 +560,12 @@ export async function atualizarMedico(
|
||||
active: input.active ?? true,
|
||||
};
|
||||
|
||||
console.log(`📤 Payload limpo:`, cleanPayload);
|
||||
console.log(` Payload limpo:`, cleanPayload);
|
||||
|
||||
// Atualizar apenas no Supabase (dados reais)
|
||||
try {
|
||||
const url = `${REST}/doctors?id=eq.${id}`;
|
||||
console.log(`🌐 URL de atualização: ${url}`);
|
||||
console.log(` URL de atualização: ${url}`);
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: "PATCH",
|
||||
@ -576,17 +576,17 @@ export async function atualizarMedico(
|
||||
body: JSON.stringify(cleanPayload),
|
||||
});
|
||||
|
||||
console.log(`📡 Resposta do servidor: ${res.status} ${res.statusText}`);
|
||||
console.log(` Resposta do servidor: ${res.status} ${res.statusText}`);
|
||||
|
||||
if (res.ok) {
|
||||
const array = await parse<Medico[] | Medico>(res);
|
||||
const result = Array.isArray(array) ? array[0] : (array as Medico);
|
||||
console.log("✅ Médico atualizado no Supabase:", result);
|
||||
console.log(" Médico atualizado no Supabase:", result);
|
||||
return result;
|
||||
} else {
|
||||
// Vamos tentar ver o erro detalhado
|
||||
const errorText = await res.text();
|
||||
console.error(`❌ Erro detalhado do Supabase:`, {
|
||||
console.error(` Erro detalhado do Supabase:`, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
response: errorText,
|
||||
@ -597,7 +597,7 @@ export async function atualizarMedico(
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Erro ao atualizar médico:", error);
|
||||
console.error(" Erro ao atualizar médico:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -610,22 +610,26 @@ export async function excluirMedico(id: string | number): Promise<void> {
|
||||
|
||||
// ===== USUÁRIOS =====
|
||||
export type AuthorizationRole = "paciente" | "medico";
|
||||
type ServerAuthorizationRole = "patient" | "doctor";
|
||||
// IMPORTANTE: O ENUM app_role no Supabase:
|
||||
// - user_roles: aceita "medico" (confirmado funcionando)
|
||||
// - patient_assignments: provavelmente aceita "user" para pacientes (baseado na API /functions/v1/create-user)
|
||||
// Valores aceitos pelo ENUM: admin | gestor | medico | secretaria | user
|
||||
type ServerAuthorizationRole = "medico" | "user";
|
||||
|
||||
const AUTHORIZATION_ROLE_TO_SERVER: Record<
|
||||
AuthorizationRole,
|
||||
ServerAuthorizationRole
|
||||
> = {
|
||||
paciente: "patient",
|
||||
medico: "doctor",
|
||||
paciente: "user", // Mapeia "paciente" interno para "user" no servidor
|
||||
medico: "medico",
|
||||
};
|
||||
|
||||
const SERVER_ROLE_TO_AUTHORIZATION: Record<
|
||||
ServerAuthorizationRole,
|
||||
AuthorizationRole
|
||||
> = {
|
||||
patient: "paciente",
|
||||
doctor: "medico",
|
||||
user: "paciente", // Mapeia "user" do servidor para "paciente" interno
|
||||
medico: "medico",
|
||||
};
|
||||
|
||||
export type UserRole = {
|
||||
@ -644,6 +648,66 @@ export async function listarUserRoles(): Promise<UserRole[]> {
|
||||
return await parse<UserRole[]>(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lista roles globais do usuário (tabela user_roles) e mapeia para AuthorizationRole
|
||||
*/
|
||||
export async function listarUserRolesDoUsuario(
|
||||
userId: string,
|
||||
): Promise<AuthorizationRole[]> {
|
||||
const url = `${REST}/user_roles?user_id=eq.${userId}`;
|
||||
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
|
||||
const rows = await parse<Array<{ role: string }>>(res);
|
||||
if (!Array.isArray(rows) || rows.length === 0) return [];
|
||||
// O banco usa "medico" e "paciente" diretamente
|
||||
return rows
|
||||
.map((r) => r.role as AuthorizationRole)
|
||||
.filter((v, i, a) => a.indexOf(v) === i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza roles globais do usuário (user_roles): remove antigas e cria novas.
|
||||
*/
|
||||
export async function atualizarUserRoles(
|
||||
userId: string,
|
||||
roles: AuthorizationRole[],
|
||||
): Promise<AuthorizationRole[]> {
|
||||
// Remove roles antigas
|
||||
const deleteUrl = `${REST}/user_roles?user_id=eq.${userId}`;
|
||||
const deleteRes = await fetch(deleteUrl, {
|
||||
method: "DELETE",
|
||||
headers: baseHeaders(),
|
||||
});
|
||||
if (!deleteRes.ok) {
|
||||
throw new Error("Não foi possível remover roles anteriores (user_roles)");
|
||||
}
|
||||
|
||||
if (roles.length === 0) return [];
|
||||
|
||||
// Mapeia para nome de role no servidor
|
||||
const payload = roles.map((r) => ({
|
||||
user_id: userId,
|
||||
role: AUTHORIZATION_ROLE_TO_SERVER[r],
|
||||
}));
|
||||
|
||||
console.log("[atualizarUserRoles] Payload:", payload);
|
||||
|
||||
const res = await fetch(`${REST}/user_roles`, {
|
||||
method: "POST",
|
||||
headers: withPrefer(
|
||||
{ ...baseHeaders(), "Content-Type": "application/json" },
|
||||
"return=representation",
|
||||
),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
const data = await parse<Array<{ role: string }>>(res);
|
||||
if (!Array.isArray(data)) return [];
|
||||
// O banco retorna "medico" e "paciente" diretamente
|
||||
return data
|
||||
.map((r) => r.role as AuthorizationRole)
|
||||
.filter((v, i, a) => a.indexOf(v) === i);
|
||||
}
|
||||
|
||||
function buildRoleFilterParameter(roles: ServerAuthorizationRole[]): string {
|
||||
const encoded = roles
|
||||
.map((role) => encodeURIComponent(`"${role}"`))
|
||||
@ -657,18 +721,90 @@ function buildRoleFilterParameter(roles: ServerAuthorizationRole[]): string {
|
||||
export async function listarAutorizacoesUsuario(
|
||||
userId: string,
|
||||
): Promise<AuthorizationRole[]> {
|
||||
console.log("[listarAutorizacoesUsuario] Buscando roles para user_id:", userId);
|
||||
|
||||
const url = `${REST}/patient_assignments?user_id=eq.${userId}`;
|
||||
const res = await fetch(url, { method: "GET", headers: baseHeaders() });
|
||||
const rows = await parse<Array<{ role: string }>>(res);
|
||||
|
||||
console.log("[listarAutorizacoesUsuario] Rows retornados:", rows);
|
||||
|
||||
if (!Array.isArray(rows) || rows.length === 0) {
|
||||
// sem atribuições, retornamos acesso padrão de paciente
|
||||
return ["paciente"];
|
||||
// SEM atribuições = SEM ACESSO ao sistema!
|
||||
// Usuário precisa ser autorizado por um admin antes de fazer login
|
||||
console.warn("[listarAutorizacoesUsuario] Usuário sem atribuições - sem acesso!");
|
||||
return [];
|
||||
}
|
||||
return rows
|
||||
.map((r) =>
|
||||
r.role === "medico" ? "medico" : ("paciente" as AuthorizationRole),
|
||||
)
|
||||
|
||||
const roles = rows
|
||||
.map((r) => {
|
||||
// Mapear role do servidor para role interna
|
||||
const serverRole = r.role as ServerAuthorizationRole;
|
||||
return SERVER_ROLE_TO_AUTHORIZATION[serverRole] || null;
|
||||
})
|
||||
.filter((role): role is AuthorizationRole => role !== null)
|
||||
.filter((role, index, self) => self.indexOf(role) === index);
|
||||
|
||||
console.log("[listarAutorizacoesUsuario] Roles finais:", roles);
|
||||
return roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica se um usuário tem permissão para fazer login como um determinado tipo
|
||||
*/
|
||||
export async function verificarPermissaoLogin(
|
||||
userId: string,
|
||||
tipoLogin: "paciente" | "profissional" | "administrador",
|
||||
): Promise<boolean> {
|
||||
console.log("[verificarPermissaoLogin] Verificando:", { userId, tipoLogin });
|
||||
|
||||
// Admin sempre pode (não usa patient_assignments)
|
||||
if (tipoLogin === "administrador") {
|
||||
console.log("[verificarPermissaoLogin] Login admin: PERMITIDO (sem validação de roles)");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Buscar roles do usuário em ambas as tabelas:
|
||||
// - patient_assignments (para pacientes)
|
||||
// - user_roles (para médicos e outros profissionais)
|
||||
const rolesPatientAssignments = await listarAutorizacoesUsuario(userId);
|
||||
const rolesUserRoles = await listarUserRolesDoUsuario(userId);
|
||||
|
||||
// Combinar roles de ambas as tabelas
|
||||
const allRoles = [...new Set([...rolesPatientAssignments, ...rolesUserRoles])];
|
||||
|
||||
console.log("[verificarPermissaoLogin] Roles em patient_assignments:", rolesPatientAssignments);
|
||||
console.log("[verificarPermissaoLogin] Roles em user_roles:", rolesUserRoles);
|
||||
console.log("[verificarPermissaoLogin] Roles combinadas:", allRoles);
|
||||
|
||||
// Se não tem nenhuma role em nenhuma tabela, bloqueia acesso
|
||||
if (allRoles.length === 0) {
|
||||
console.warn(
|
||||
"[verificarPermissaoLogin] BLOQUEADO - Usuário sem atribuições",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mapear tipo de login para role necessária
|
||||
if (tipoLogin === "profissional") {
|
||||
const temPermissao = allRoles.includes("medico");
|
||||
console.log(
|
||||
"[verificarPermissaoLogin] Login médico:",
|
||||
temPermissao ? "PERMITIDO" : "BLOQUEADO",
|
||||
);
|
||||
return temPermissao;
|
||||
}
|
||||
|
||||
if (tipoLogin === "paciente") {
|
||||
const temPermissao = allRoles.includes("paciente");
|
||||
console.log(
|
||||
"[verificarPermissaoLogin] Login paciente:",
|
||||
temPermissao ? "PERMITIDO" : "BLOQUEADO",
|
||||
);
|
||||
return temPermissao;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -695,10 +831,11 @@ export async function atualizarAutorizacoesUsuario(
|
||||
}
|
||||
|
||||
// Cria novas atribuições (IMPORTANTE: patient_id é obrigatório!)
|
||||
// Mapear roles para os valores aceitos pelo servidor
|
||||
const payload = roles.map((role) => ({
|
||||
user_id: userId,
|
||||
patient_id: patientId,
|
||||
role
|
||||
role: AUTHORIZATION_ROLE_TO_SERVER[role],
|
||||
}));
|
||||
|
||||
console.log("[atualizarAutorizacoesUsuario] Payload:", payload);
|
||||
@ -714,9 +851,12 @@ export async function atualizarAutorizacoesUsuario(
|
||||
const data = await parse<Array<{ role: string }>>(res);
|
||||
if (!Array.isArray(data)) return [];
|
||||
return data
|
||||
.map((r) =>
|
||||
r.role === "medico" ? "medico" : ("paciente" as AuthorizationRole),
|
||||
)
|
||||
.map((r) => {
|
||||
// Mapear role do servidor para role interna
|
||||
const serverRole = r.role as ServerAuthorizationRole;
|
||||
return SERVER_ROLE_TO_AUTHORIZATION[serverRole] || null;
|
||||
})
|
||||
.filter((role): role is AuthorizationRole => role !== null)
|
||||
.filter((v, index, a) => a.indexOf(v) === index);
|
||||
}
|
||||
|
||||
@ -870,11 +1010,11 @@ export async function criarUsuarioMedico(medico: {
|
||||
}): Promise<CreateUserWithPasswordResponse> {
|
||||
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);
|
||||
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`;
|
||||
@ -889,7 +1029,7 @@ export async function criarUsuarioMedico(medico: {
|
||||
},
|
||||
};
|
||||
|
||||
console.log("📤 [CRIAR MÉDICO] Enviando para:", signupUrl);
|
||||
console.log("[CRIAR MÉDICO] Enviando para:", signupUrl);
|
||||
|
||||
try {
|
||||
const response = await fetch(signupUrl, {
|
||||
@ -903,14 +1043,14 @@ export async function criarUsuarioMedico(medico: {
|
||||
});
|
||||
|
||||
console.log(
|
||||
"📋 [CRIAR MÉDICO] Status da resposta:",
|
||||
"[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);
|
||||
const errorText = await response.text();
|
||||
console.error("[CRIAR MÉDICO] Erro na resposta:", errorText);
|
||||
|
||||
// Tenta parsear o erro para pegar mensagem específica
|
||||
let errorMessage = `Erro ao criar usuário (${response.status})`;
|
||||
@ -942,24 +1082,24 @@ export async function criarUsuarioMedico(medico: {
|
||||
|
||||
const responseData = await response.json();
|
||||
console.log(
|
||||
"✅ [CRIAR MÉDICO] Usuário criado com sucesso no Supabase Auth!",
|
||||
"[CRIAR MÉDICO] Usuário criado com sucesso no Supabase Auth!",
|
||||
);
|
||||
console.log("🆔 User ID:", responseData.user?.id || responseData.id);
|
||||
console.log("User ID:", responseData.user?.id || responseData.id);
|
||||
|
||||
// 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
|
||||
// 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...",
|
||||
"[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...",
|
||||
"[AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
|
||||
);
|
||||
|
||||
const loginResponse = await fetch(loginUrl, {
|
||||
@ -977,11 +1117,11 @@ export async function criarUsuarioMedico(medico: {
|
||||
if (loginResponse.ok) {
|
||||
const loginData = await loginResponse.json();
|
||||
console.log(
|
||||
"✅ [AUTO-CONFIRMAR] Login automático realizado com sucesso!",
|
||||
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
|
||||
);
|
||||
console.log(
|
||||
"📦 [AUTO-CONFIRMAR] Email confirmado:",
|
||||
loginData.user?.email_confirmed_at ? "SIM ✅" : "NÃO ❌",
|
||||
"[AUTO-CONFIRMAR] Email confirmado:",
|
||||
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
|
||||
);
|
||||
|
||||
// Atualizar responseData com dados do login (que tem email confirmado)
|
||||
@ -991,39 +1131,37 @@ export async function criarUsuarioMedico(medico: {
|
||||
} else {
|
||||
const errorText = await loginResponse.text();
|
||||
console.error(
|
||||
"❌ [AUTO-CONFIRMAR] Falha no login automático:",
|
||||
"[AUTO-CONFIRMAR] Falha no login automático:",
|
||||
loginResponse.status,
|
||||
errorText,
|
||||
);
|
||||
console.warn(
|
||||
"⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
|
||||
"[AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
|
||||
);
|
||||
}
|
||||
} catch (confirmError) {
|
||||
console.error(
|
||||
"❌ [AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
|
||||
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
|
||||
confirmError,
|
||||
);
|
||||
console.warn(
|
||||
"⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...",
|
||||
"[AUTO-CONFIRMAR] Continuando sem confirmação automática...",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log("✅ [CRIAR MÉDICO] Email confirmado automaticamente!");
|
||||
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("========================================");
|
||||
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)",
|
||||
"Pode fazer login?",
|
||||
responseData.user?.email_confirmed_at ? "SIM" : "NÃO (precisa confirmar email)",
|
||||
);
|
||||
console.log("🔐 ========================================");
|
||||
console.log("========================================");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@ -1032,7 +1170,7 @@ export async function criarUsuarioMedico(medico: {
|
||||
password: senha,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("❌ [CRIAR MÉDICO] Erro ao criar usuário:", error);
|
||||
console.error("[CRIAR MÉDICO] Erro ao criar usuário:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -1045,11 +1183,11 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
}): Promise<CreateUserWithPasswordResponse> {
|
||||
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);
|
||||
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`;
|
||||
@ -1064,7 +1202,7 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
},
|
||||
};
|
||||
|
||||
console.log("📤 [CRIAR PACIENTE] Enviando para:", signupUrl);
|
||||
console.log("[CRIAR PACIENTE] Enviando para:", signupUrl);
|
||||
|
||||
try {
|
||||
const response = await fetch(signupUrl, {
|
||||
@ -1078,14 +1216,14 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
});
|
||||
|
||||
console.log(
|
||||
"📋 [CRIAR PACIENTE] Status da resposta:",
|
||||
"[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);
|
||||
const errorText = await response.text();
|
||||
console.error("[CRIAR PACIENTE] Erro na resposta:", errorText);
|
||||
|
||||
// Tenta parsear o erro para pegar mensagem específica
|
||||
let errorMessage = `Erro ao criar usuário (${response.status})`;
|
||||
@ -1117,17 +1255,17 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
|
||||
const responseData = await response.json();
|
||||
console.log(
|
||||
"✅ [CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!",
|
||||
"[CRIAR PACIENTE] Usuário criado com sucesso no Supabase Auth!",
|
||||
);
|
||||
console.log("🆔 User ID:", responseData.user?.id || responseData.id);
|
||||
console.log("User ID:", responseData.user?.id || responseData.id);
|
||||
console.log(
|
||||
"📦 [CRIAR PACIENTE] Resposta completa do Supabase:",
|
||||
JSON.stringify(responseData, null, 2),
|
||||
"[CRIAR PACIENTE] Resposta completa do Supabase:",
|
||||
JSON.stringify(responseData, undefined, 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("AVISO: Supabase retornou sucesso mas SEM user ID!");
|
||||
console.error(
|
||||
"Isso pode significar que o usuário NÃO foi criado de verdade!",
|
||||
);
|
||||
@ -1135,20 +1273,20 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
|
||||
const userId = responseData.user?.id || responseData.id;
|
||||
|
||||
// 🔧 AUTO-CONFIRMAR EMAIL: Fazer login automático logo após criar usuário
|
||||
// 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...",
|
||||
"[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...",
|
||||
"[AUTO-CONFIRMAR] Fazendo login automático para confirmar email...",
|
||||
);
|
||||
|
||||
const loginResponse = await fetch(loginUrl, {
|
||||
@ -1164,29 +1302,29 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
});
|
||||
|
||||
console.log(
|
||||
"🔍 [AUTO-CONFIRMAR] Status do login automático:",
|
||||
"[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!",
|
||||
"[AUTO-CONFIRMAR] Login automático realizado com sucesso!",
|
||||
);
|
||||
console.log(
|
||||
"📦 [AUTO-CONFIRMAR] Dados completos do login:",
|
||||
"[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 ❌",
|
||||
"[AUTO-CONFIRMAR] Email confirmado:",
|
||||
loginData.user?.email_confirmed_at ? "SIM" : "NÃO",
|
||||
);
|
||||
console.log(
|
||||
"👤 [AUTO-CONFIRMAR] UserType no metadata:",
|
||||
"[AUTO-CONFIRMAR] UserType no metadata:",
|
||||
loginData.user?.user_metadata?.userType,
|
||||
);
|
||||
console.log(
|
||||
"🎯 [AUTO-CONFIRMAR] Email verified:",
|
||||
"[AUTO-CONFIRMAR] Email verified:",
|
||||
loginData.user?.user_metadata?.email_verified,
|
||||
);
|
||||
|
||||
@ -1197,48 +1335,46 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
} else {
|
||||
const errorText = await loginResponse.text();
|
||||
console.error(
|
||||
"❌ [AUTO-CONFIRMAR] Falha no login automático:",
|
||||
"[AUTO-CONFIRMAR] Falha no login automático:",
|
||||
loginResponse.status,
|
||||
errorText,
|
||||
);
|
||||
console.warn(
|
||||
"⚠️ [AUTO-CONFIRMAR] Usuário pode não conseguir fazer login imediatamente!",
|
||||
"[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);
|
||||
console.error("[AUTO-CONFIRMAR] Detalhes do erro:", errorData);
|
||||
} catch (error) {
|
||||
console.error("[AUTO-CONFIRMAR] Erro não é JSON:", errorText);
|
||||
}
|
||||
}
|
||||
} catch (confirmError) {
|
||||
console.error(
|
||||
"❌ [AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
|
||||
"[AUTO-CONFIRMAR] Erro ao tentar fazer login automático:",
|
||||
confirmError,
|
||||
);
|
||||
console.warn(
|
||||
"⚠️ [AUTO-CONFIRMAR] Continuando sem confirmação automática...",
|
||||
"[AUTO-CONFIRMAR] Continuando sem confirmação automática...",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log("✅ [CRIAR PACIENTE] Email confirmado automaticamente!");
|
||||
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("========================================");
|
||||
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)",
|
||||
"Pode fazer login?",
|
||||
responseData.user?.email_confirmed_at ? "SIM" : "NÃO (precisa confirmar email)",
|
||||
);
|
||||
console.log("🔐 ========================================");
|
||||
console.log("========================================");
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@ -1247,7 +1383,7 @@ export async function criarUsuarioPaciente(paciente: {
|
||||
password: senha,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("❌ [CRIAR PACIENTE] Erro ao criar usuário:", error);
|
||||
console.error("[CRIAR PACIENTE] Erro ao criar usuário:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
} from "@/lib/config";
|
||||
import { debugRequest } from "@/lib/debug-utils";
|
||||
import { ENV_CONFIG } from "@/lib/env-config";
|
||||
import { verificarPermissaoLogin } from "@/lib/api";
|
||||
|
||||
/**
|
||||
* Classe de erro customizada para autenticação
|
||||
@ -179,6 +180,52 @@ export async function loginUser(
|
||||
);
|
||||
}
|
||||
|
||||
// VALIDAÇÃO CRÍTICA: Verificar se o usuário tem permissão para fazer login com este tipo
|
||||
const userId = data.user?.id || data.id;
|
||||
|
||||
if (userId) {
|
||||
console.log("[AUTH] Verificando permissões em patient_assignments...");
|
||||
|
||||
const temPermissao = await verificarPermissaoLogin(userId, userType);
|
||||
|
||||
if (!temPermissao) {
|
||||
console.error(
|
||||
"[AUTH] ACESSO NEGADO - Usuário sem permissão para este tipo de login!",
|
||||
{
|
||||
userId,
|
||||
tipoLogin: userType,
|
||||
},
|
||||
);
|
||||
|
||||
// Mensagem específica por tipo de login
|
||||
let mensagemErro = "";
|
||||
|
||||
if (userType === "paciente") {
|
||||
mensagemErro =
|
||||
"Você não tem permissão para acessar a área de pacientes. " +
|
||||
"Um administrador precisa autorizar seu acesso primeiro. " +
|
||||
"Entre em contato com a recepção ou administração do sistema.";
|
||||
} else if (userType === "profissional") {
|
||||
mensagemErro =
|
||||
"Você não tem permissão para acessar a área de profissionais. " +
|
||||
"Um administrador precisa autorizar seu acesso primeiro. " +
|
||||
"Entre em contato com a administração do sistema.";
|
||||
} else {
|
||||
mensagemErro =
|
||||
"Você não tem permissão para acessar esta área do sistema. " +
|
||||
"Entre em contato com o administrador.";
|
||||
}
|
||||
|
||||
throw new AuthenticationError(
|
||||
mensagemErro,
|
||||
"ACESSO_NEGADO",
|
||||
{ userId, tipoLogin: userType },
|
||||
);
|
||||
}
|
||||
|
||||
console.log("[AUTH] Permissões validadas com sucesso!");
|
||||
}
|
||||
|
||||
// Adaptar resposta da sua API para o formato esperado
|
||||
const adaptedResponse: LoginResponse = {
|
||||
access_token: data.access_token || data.token,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user