132 lines
3.8 KiB
Svelte
132 lines
3.8 KiB
Svelte
<script lang="ts">
|
|
import { useQuery } from 'convex-svelte';
|
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
|
import { onMount } from 'svelte';
|
|
import type { Snippet } from 'svelte';
|
|
|
|
let {
|
|
children,
|
|
requireAuth = true,
|
|
allowedRoles = [],
|
|
maxLevel = 3,
|
|
redirectTo = '/'
|
|
}: {
|
|
children: Snippet;
|
|
requireAuth?: boolean;
|
|
allowedRoles?: string[];
|
|
maxLevel?: number;
|
|
redirectTo?: string;
|
|
} = $props();
|
|
|
|
let isChecking = $state(true);
|
|
let hasAccess = $state(false);
|
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
let hasCheckedOnce = $state(false);
|
|
let lastUserState = $state<typeof currentUser | undefined>(undefined);
|
|
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
|
|
|
// Usar $effect para reagir apenas às mudanças na query currentUser
|
|
$effect(() => {
|
|
// Não verificar novamente se já tem acesso concedido e usuário está autenticado
|
|
if (hasAccess && currentUser?.data) {
|
|
lastUserState = currentUser;
|
|
return;
|
|
}
|
|
|
|
// Evitar loop: só verificar se currentUser realmente mudou
|
|
// Comparar dados, não o objeto proxy
|
|
const currentData = currentUser?.data;
|
|
const lastData = lastUserState?.data;
|
|
if (currentData !== lastData || (currentUser === undefined) !== (lastUserState === undefined)) {
|
|
lastUserState = currentUser;
|
|
checkAccess();
|
|
}
|
|
});
|
|
|
|
function checkAccess() {
|
|
// Limpar timeout anterior se existir
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = null;
|
|
}
|
|
|
|
// Se a query ainda está carregando (undefined), aguardar
|
|
if (currentUser === undefined) {
|
|
isChecking = true;
|
|
hasAccess = false;
|
|
return;
|
|
}
|
|
|
|
// Marcar que já verificou pelo menos uma vez
|
|
hasCheckedOnce = true;
|
|
|
|
// Se a query retornou dados, verificar autenticação
|
|
if (currentUser?.data) {
|
|
// Verificar roles
|
|
if (allowedRoles.length > 0) {
|
|
const hasRole = allowedRoles.includes(currentUser.data.role?.nome ?? '');
|
|
if (!hasRole) {
|
|
const currentPath = window.location.pathname;
|
|
window.location.href = `${redirectTo}?error=access_denied&route=${encodeURIComponent(currentPath)}`;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Verificar nível
|
|
if (currentUser.data.role?.nivel && currentUser.data.role.nivel > maxLevel) {
|
|
const currentPath = window.location.pathname;
|
|
window.location.href = `${redirectTo}?error=access_denied&route=${encodeURIComponent(currentPath)}`;
|
|
return;
|
|
}
|
|
|
|
// Se chegou aqui, permitir acesso
|
|
hasAccess = true;
|
|
isChecking = false;
|
|
return;
|
|
}
|
|
|
|
// Se não tem dados e requer autenticação
|
|
if (requireAuth && !currentUser?.data) {
|
|
// Se a query já retornou (não está mais undefined), finalizar estado
|
|
if (currentUser !== undefined) {
|
|
const currentPath = window.location.pathname;
|
|
// Evitar redirecionamento em loop - verificar se já está na URL de erro
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
if (!urlParams.has('error')) {
|
|
// Só redirecionar se não estiver em loop
|
|
if (!hasCheckedOnce || currentUser === null) {
|
|
window.location.href = `${redirectTo}?error=auth_required&redirect=${encodeURIComponent(currentPath)}`;
|
|
return;
|
|
}
|
|
}
|
|
// Se já tem erro na URL, permitir renderização para mostrar o alerta
|
|
isChecking = false;
|
|
hasAccess = true;
|
|
return;
|
|
}
|
|
|
|
// Se ainda está carregando (undefined), aguardar
|
|
isChecking = true;
|
|
hasAccess = false;
|
|
return;
|
|
}
|
|
|
|
// Se não requer autenticação, permitir acesso
|
|
if (!requireAuth) {
|
|
hasAccess = true;
|
|
isChecking = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{#if isChecking}
|
|
<div class="flex min-h-screen items-center justify-center">
|
|
<div class="text-center">
|
|
<span class="loading loading-spinner loading-lg text-primary"></span>
|
|
<p class="text-base-content/70 mt-4">Verificando permissões...</p>
|
|
</div>
|
|
</div>
|
|
{:else if hasAccess}
|
|
{@render children()}
|
|
{/if}
|