📚 DOCUMENTAÇÃO TÉCNICA COMPLETA - E-VOLUA
Sistema de Avaliação de Desempenho na Prática de Alunos
Versão: 1.0.0
Data: Outubro de 2025
Repositório: github.com/julioventura/evolua
Domínio: cirurgia.com.br/evolua
📋 ÍNDICE
- Visão Geral
- Especificações Técnicas
- Arquitetura e Infraestrutura
- Configuração do Ambiente
- Estrutura do Banco de Dados
- Sistema de Autenticação
- Funcionalidades Principais
- Segurança Implementada
- Guia de Replicação
- Resolução de Problemas
🎯 VISÃO GERAL
O e-volua é uma plataforma web moderna para avaliação de desempenho de alunos em aulas práticas, especialmente voltada para contextos médicos e de saúde. O sistema permite que professores criem avaliações, gerenciem turmas e acompanhem o progresso dos alunos em tempo real.
Principais Características
- ✅ Sistema completo de gestão de turmas e alunos
- ✅ Avaliações periódicas estruturadas
- ✅ Dashboard interativo com estatísticas em tempo real
- ✅ Sistema de autenticação robusto com múltiplos papéis de usuário
- ✅ Interface responsiva e moderna
- ✅ Armazenamento seguro de dados com PostgreSQL
- ✅ Integração com automações via N8N
💻 ESPECIFICAÇÕES TÉCNICAS
Frontend e Build
Linguagem de Programação
- TypeScript
5.8.3- Linguagem fortemente tipada baseada em JavaScript
- Configuração strict mode para máxima segurança de tipos
- Target: ES2022 (ECMAScript 2022)
- Module System: ESNext
Framework e Bibliotecas React
- React
19.1.0- Biblioteca para construção de interfaces - React DOM
19.1.0- Integração com navegador - React Router DOM
7.6.3- Sistema de roteamento SPA
Ferramenta de Build
- Vite
7.0.0- Build tool de alta performance
- Hot Module Replacement (HMR)
- Otimização automática de bundles
- Code splitting inteligente
Framework de Estilização
- Tailwind CSS
4.1.11- Framework utility-first
- Customização via
tailwind.config.js - Paleta de cores personalizada (primary blue)
- PostCSS
8.5.6- Processador CSS - Autoprefixer
10.4.21- Compatibilidade cross-browser
Bibliotecas de UI e Utilitários
- @heroicons/react
2.2.0- Ícones SVG otimizados - date-fns
4.1.0- Manipulação de datas - clsx
2.1.1- Construção condicional de classes - tailwind-merge
3.3.1- Merge de classes Tailwind sem conflitos
Backend e Banco de Dados
Plataforma Backend
-
Supabase (Auto-hospedado/Open Source)
- PostgreSQL 15+ para banco de dados
- Autenticação integrada (JWT)
- Row Level Security (RLS)
- Real-time subscriptions
- Storage para arquivos
-
@supabase/supabase-js
2.50.3- Cliente JavaScript oficial
- Suporte a persistência de sessão
- Auto-refresh de tokens
- Detecção automática de sessão
Infraestrutura e Hospedagem
Servidor VPS (Auto-hospedado)
-
Supabase Open Source
- PostgreSQL database
- PostgREST API
- GoTrue authentication
- Realtime server
- Storage server
-
N8N (Workflow Automation)
- Automação de processos
- Integrações com serviços externos
- Triggers e webhooks
-
Apache Web Server
- Hospedagem do domínio cirurgia.com.br
- Configuração SSL/TLS
- Proxy reverso para Supabase
Ferramentas de Desenvolvimento
IDE e Editor
- Visual Studio Code (VSCode)
- Editor gratuito da Microsoft
- Extensões TypeScript nativas
- Integrated terminal
- Git integration
Assistência de IA (GitHub Copilot)
Assinatura profissional com acesso aos seguintes modelos:
Anthropic Claude:
- Claude Sonnet 3.7
- Claude Sonnet 4
- Claude Sonnet 4.5
OpenAI:
- GPT-4o
- GPT-4.1
- GPT-5
Controle de Versão
- Git - Sistema de controle de versão
- GitHub.com (Microsoft)
- Repositório remoto
- Controle de versões
- Backup automático online
- CI/CD pipelines (se configurado)
Qualidade de Código
Linting e Análise Estática
- ESLint
9.29.0- Análise estática de código
- Detecção de erros e bad practices
- TypeScript ESLint
8.34.1- Regras específicas para TypeScript
- eslint-plugin-react-hooks
5.2.0- Validação de React Hooks
- eslint-plugin-react-refresh
0.4.20- Suporte para Fast Refresh
🏗️ ARQUITETURA E INFRAESTRUTURA
Arquitetura da Aplicação
┌─────────────────────────────────────────────────────────┐
│ FRONTEND (React) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Dashboard │ │ Turmas │ │ Avaliações │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Login │ │ Membros │ │ Relatórios │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
HTTPS/REST API
│
┌─────────────────────────────────────────────────────────┐
│ SUPABASE (VPS Auto-hospedado) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ GoTrue │ │ PostgREST │ │
│ │ Database │ │ Auth │ │ REST API │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Realtime │ │ Storage │ │
│ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│
Automações
│
┌─────────────────────────────────────────────────────────┐
│ N8N WORKFLOWS │
│ • Notificações automáticas │
│ • Sincronização de dados │
│ • Relatórios agendados │
└─────────────────────────────────────────────────────────┘
Estrutura de Diretórios
evolua/
├── public/ # Arquivos estáticos públicos
│ ├── favicon.svg
│ └── assets/
│
├── sql/ # Scripts SQL do banco de dados
│ ├── avaliacoes_periodicas.sql
│ ├── create_user_configs.sql
│ └── ...
│
├── src/ # Código-fonte principal
│ ├── assets/ # Imagens e recursos
│ │
│ ├── components/ # Componentes React
│ │ ├── features/ # Componentes específicos
│ │ ├── layout/ # Componentes de layout
│ │ ├── ui/ # Componentes reutilizáveis
│ │ ├── ChatbotModal.tsx
│ │ ├── ProtectedRoute.tsx
│ │ └── SecurityGuard.tsx
│ │
│ ├── config/ # Configurações
│ │ └── roleConfig.ts # Configuração de papéis
│ │
│ ├── contexts/ # Contextos React
│ │ ├── AuthContext.tsx
│ │ ├── AuthContext.types.ts
│ │ ├── SettingsContext.tsx
│ │ └── ThemeContext.tsx
│ │
│ ├── hooks/ # Custom Hooks
│ │ ├── useAuth.ts
│ │ ├── useProfile.ts
│ │ ├── useRoleMonitor.ts
│ │ ├── useTurmas.ts
│ │ └── useUserValidation.ts
│ │
│ ├── lib/ # Serviços e utilitários
│ │ ├── supabaseClient.ts # Cliente Supabase
│ │ ├── turmasService.ts
│ │ ├── avaliacoesService.ts
│ │ ├── avaliacoesPeriodicas.ts
│ │ ├── profileService.ts
│ │ ├── roleValidation.ts
│ │ └── utils.ts
│ │
│ ├── pages/ # Páginas da aplicação
│ │ ├── DashboardPage.tsx
│ │ ├── LoginPage.tsx
│ │ ├── TurmasPage.tsx
│ │ ├── AvaliacoesPage.tsx
│ │ └── ...
│ │
│ ├── types/ # Definições TypeScript
│ │ ├── supabase.ts
│ │ └── ...
│ │
│ ├── App.tsx # Componente principal
│ ├── main.tsx # Ponto de entrada
│ └── types.ts # Tipos globais
│
├── .env.example # Exemplo de variáveis de ambiente
├── .gitignore # Arquivos ignorados pelo Git
├── index.html # HTML principal
├── package.json # Dependências do projeto
├── tsconfig.json # Configuração TypeScript
├── vite.config.ts # Configuração Vite
└── tailwind.config.js # Configuração Tailwind
Otimizações de Build
O projeto implementa code splitting estratégico:
Separação de Vendors
vendor-react: React, React DOM, React Routervendor-supabase: Cliente Supabasevendor-heroicons: Íconesvendor-date: date-fnsvendor-other: Outras dependências
Separação de Páginas
page-dashboard: Página principalpage-avaliacoes: Sistema de avaliaçõespage-turmas: Gestão de turmaspage-membros: Gestão de membrospages-other: Outras páginas
Separação de Serviços
services: Serviços de turmas, avaliações e periódicas
Limite de Warning: 600KB por chunk
⚙️ CONFIGURAÇÃO DO AMBIENTE
Pré-requisitos
- Node.js >= 18.0.0
- npm >= 9.0.0 ou yarn >= 1.22.0
- Git >= 2.30.0
- Conta Supabase (ou instalação auto-hospedada)
Instalação Passo a Passo
1. Clonar o Repositório
git clone https://github.com/julioventura/evolua.git
cd evolua
2. Instalar Dependências
npm install
3. Configurar Variáveis de Ambiente
Criar arquivo .env na raiz do projeto:
# Windows
copy .env.example .env
# Linux/Mac
cp .env.example .env
Editar .env com suas credenciais:
# Supabase Configuration
VITE_SUPABASE_URL=https://seu-projeto.supabase.co
VITE_SUPABASE_ANON_KEY=eyJ... (chave completa)
Como obter as credenciais:
- Acesse supabase.com
- Vá em Project Settings → API
- Copie Project URL e anon public key
4. Executar em Desenvolvimento
npm run dev
Windows (usando batch file):
run.bat
Aplicação disponível em: http://localhost:5173
5. Build para Produção
npm run build
Windows:
build.bat
Arquivos gerados em: dist/
6. Preview da Build
npm run preview
🗄️ ESTRUTURA DO BANCO DE DADOS
Tabelas Principais
1. profiles - Perfis de Usuário
CREATE TABLE profiles (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
nome TEXT NOT NULL,
email TEXT,
whatsapp TEXT,
cidade TEXT,
estado TEXT,
avatar_url TEXT,
categoria TEXT NOT NULL DEFAULT 'aluno'
CHECK (categoria IN ('aluno', 'professor', 'admin', 'monitor', 'outro')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
PRIMARY KEY (id)
);
Papéis de Usuário:
admin: Acesso total ao sistemaprofessor: Gerencia turmas e avaliaçõesmonitor: Auxilia em turmas específicasaluno: Participa de turmas e responde avaliaçõesoutro: Sem permissões (estado transitório)
2. turmas - Gestão de Turmas
CREATE TABLE turmas (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
nome TEXT NOT NULL,
descricao TEXT,
professor_id UUID REFERENCES profiles(id),
codigo_acesso TEXT UNIQUE,
ativa BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
3. turma_membros - Relacionamento Turma-Aluno
CREATE TABLE turma_membros (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
turma_id UUID REFERENCES turmas(id) ON DELETE CASCADE,
aluno_id UUID REFERENCES profiles(id) ON DELETE CASCADE,
papel TEXT DEFAULT 'aluno' CHECK (papel IN ('aluno', 'monitor')),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(turma_id, aluno_id)
);
4. formularios_avaliacao - Templates de Avaliação
CREATE TABLE formularios_avaliacao (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
titulo TEXT NOT NULL,
descricao TEXT,
professor_id UUID REFERENCES profiles(id),
schema_json JSONB NOT NULL,
ativo BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
5. instancias_avaliacao - Avaliações Distribuídas
CREATE TABLE instancias_avaliacao (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
formulario_id UUID REFERENCES formularios_avaliacao(id),
aluno_id UUID REFERENCES profiles(id),
turma_id UUID REFERENCES turmas(id),
status TEXT DEFAULT 'pendente'
CHECK (status IN ('pendente', 'iniciada', 'concluida', 'vencida')),
data_inicio TIMESTAMP WITH TIME ZONE,
data_limite TIMESTAMP WITH TIME ZONE,
data_conclusao TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
6. respostas_avaliacao - Respostas dos Alunos
CREATE TABLE respostas_avaliacao (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
instancia_id UUID REFERENCES instancias_avaliacao(id) ON DELETE CASCADE,
respostas_json JSONB NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
Triggers e Funções
🔴 CRÍTICO: Auto-criação de Profile
⚠️ Este trigger é ESSENCIAL para o cadastro funcionar!
Sem ele, você receberá o erro: Database error saving new user
Script completo: sql/create_profile_trigger.sql ou sql/FIX_URGENTE_profile_trigger.sql
-- Função que cria profile automaticamente
CREATE OR REPLACE FUNCTION public.evolua_handle_new_user()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
v_nome TEXT;
v_categoria TEXT;
BEGIN
-- Extrair dados do metadata
v_nome := COALESCE(
NEW.raw_user_meta_data->>'nome',
NEW.raw_user_meta_data->>'full_name',
split_part(NEW.email, '@', 1)
);
v_categoria := COALESCE(
NEW.raw_user_meta_data->>'categoria',
'aluno'
);
-- Inserir profile
INSERT INTO public.profiles (
id, nome, email, categoria, created_at, updated_at
) VALUES (
NEW.id, v_nome, NEW.email, v_categoria, NOW(), NOW()
)
ON CONFLICT (id) DO UPDATE SET
nome = EXCLUDED.nome,
email = EXCLUDED.email,
updated_at = NOW();
RETURN NEW;
EXCEPTION
WHEN OTHERS THEN
RAISE WARNING 'Erro ao criar profile: % - %', SQLSTATE, SQLERRM;
RETURN NEW;
END;
$$;
-- Trigger que dispara a função
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE FUNCTION public.evolua_handle_new_user();
Verificar se existe:
SELECT trigger_name, event_object_table
FROM information_schema.triggers
WHERE trigger_name = 'on_auth_user_created';
Row Level Security (RLS)
Políticas de Segurança para profiles
-- Habilitar RLS
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Usuários podem ver seu próprio perfil
CREATE POLICY "Users can view their own profile"
ON profiles FOR SELECT
USING (auth.uid() = id);
-- Usuários podem atualizar seu próprio perfil
CREATE POLICY "Users can update their own profile"
ON profiles FOR UPDATE
USING (auth.uid() = id);
-- Admins podem ver todos os perfis
CREATE POLICY "Admins can view all profiles"
ON profiles FOR SELECT
USING (
EXISTS (
SELECT 1 FROM profiles
WHERE id = auth.uid() AND categoria = 'admin'
)
);
Políticas para turmas
ALTER TABLE turmas ENABLE ROW LEVEL SECURITY;
-- Professores veem suas turmas
CREATE POLICY "Professors can view their turmas"
ON turmas FOR SELECT
USING (professor_id = auth.uid());
-- Alunos veem turmas em que estão matriculados
CREATE POLICY "Students can view their enrolled turmas"
ON turmas FOR SELECT
USING (
id IN (
SELECT turma_id FROM turma_membros
WHERE aluno_id = auth.uid()
)
);
🔐 SISTEMA DE AUTENTICAÇÃO
Arquitetura de Autenticação
O sistema utiliza múltiplas camadas de segurança:
1. AuthProvider (Context API)
Arquivo: src/contexts/AuthProviderEmergency.tsx
Responsabilidades:
- Gerenciar estado de autenticação
- Carregar perfil do usuário
- Sincronizar sessão com Supabase
- Implementar retry logic com backoff exponencial
- Cache multi-camadas de perfil
Características:
interface AuthContextType {
user: AppUser | null;
loading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
signup: (data: SignupData) => Promise<void>;
}
2. Proteção de Rotas
Arquivo: src/components/ProtectedRoute.tsx
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
Funcionalidades:
- Verifica autenticação antes de renderizar
- Redireciona para login se não autenticado
- Exibe loading durante verificação
- Integra com useUserValidation
3. Validação de Usuário
Arquivo: src/hooks/useUserValidation.ts
const { isValid, error } = useUserValidation();
Validações:
- Detecta categoria "outro" (inválida)
- Força logout de usuários inválidos
- Redireciona para login com mensagem
- Log de auditoria de validações
4. Monitor de Papéis
Arquivo: src/hooks/useRoleMonitor.ts
const { hasPermission, userRole } = useRoleMonitor();
Funcionalidades:
- Detecta mudanças suspeitas de papel
- Alerta sobre escalações não autorizadas
- Log de segurança para auditoria
- Prevenção de privilege escalation
5. SecurityGuard Component
Arquivo: src/components/SecurityGuard.tsx
<SecurityGuard requiredRoles={['professor', 'admin']}>
<ConteudoProtegido />
</SecurityGuard>
Proteção granular por componente:
- Validação de permissões em tempo real
- Controle de acesso baseado em papéis
- Mensagens customizadas de acesso negado
Fluxo de Autenticação
1. USUÁRIO ACESSA APLICAÇÃO
↓
2. AuthProvider.useEffect() INICIA
↓
3. Verifica sessão no Supabase
↓
4. Se autenticado:
├─→ Carrega perfil (com retry/cache)
├─→ Valida categoria !== 'outro'
├─→ Atualiza contexto
└─→ Libera acesso
↓
5. Se não autenticado:
└─→ Redireciona para /login
↓
6. LOGIN:
├─→ Supabase.auth.signInWithPassword()
├─→ Carrega perfil completo
├─→ Valida categoria
└─→ Redireciona para dashboard
↓
7. MONITORAMENTO CONTÍNUO:
├─→ useRoleMonitor detecta mudanças
├─→ useUserValidation valida estado
└─→ SecurityGuard protege componentes
Estratégia de Cache
Cache Multi-Camada
Camada 1: Memória (5 minutos)
let profileCache: { data: Profile; timestamp: number } | null = null;
Camada 2: localStorage (persistente)
localStorage.setItem('user_profile_cache', JSON.stringify(profile));
Camada 3: Supabase (source of truth)
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', userId)
.single();
Retry Logic com Backoff Exponencial
const delays = [1000, 2000, 4000]; // 1s, 2s, 4s
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await loadProfile();
} catch (error) {
if (attempt < 2) {
await sleep(delays[attempt]);
}
}
}
Segurança de Fallbacks
*❌ NUNCA usar "professor" como fallback
// ERRADO
categoria: 'professor' // PERIGOSO!
// CORRETO
categoria: 'outro' // Será bloqueado pela validação
Health Check do Supabase
const checkSupabaseHealth = async (): Promise<boolean> => {
try {
const { error } = await supabase
.from('profiles')
.select('id')
.limit(1);
return !error;
} catch {
return false;
}
};
🎨 FUNCIONALIDADES PRINCIPAIS
1. Dashboard Interativo
Arquivo: src/pages/DashboardPage.tsx
Cards de Estatísticas
- Avaliações: Total e filtros por status
- Turmas: Ativas e arquivadas
- Alunos: Total matriculados
- Atividades Recentes: Timeline de ações
Modais Interativos
Todos os cards abrem modais detalhados:
- Lista completa de items
- Filtros e busca
- Ações rápidas (criar, editar, excluir)
- Modo maximizado (tela cheia)
Ações Rápidas
- 📝 Nova Avaliação
- 👥 Gerenciar Alunos
- 📊 Gerar Relatórios
- 📁 Arquivos de Referência (Google Drive)
Card Exclusivo para Admins
- 👤 Usuários: Visível apenas para admins
- Lista todos os usuários por categoria
- Detalhes completos de perfil
- Gerenciamento de permissões
2. Gestão de Turmas
Arquivo: src/pages/TurmasPage.tsx
Funcionalidades
- Criar/editar/arquivar turmas
- Código de acesso único por turma
- Adicionar/remover alunos
- Definir monitores
- Visualizar estatísticas da turma
Configuração por Papel
// Admin vê todas as turmas
// Professor vê suas turmas
// Monitor vê turmas que monitora
// Aluno vê turmas matriculadas
3. Sistema de Avaliações Periódicas
Arquivo: src/pages/AvaliacoesPage.tsx
Para Professores/Monitores
Criar Formulário:
const formulario = {
titulo: "Avaliação Prática Diária",
descricao: "Avaliação de desempenho em procedimentos",
schema_json: {
paginas: [
{
titulo: "Dados do Paciente",
campos: [/* ... */]
},
{
titulo: "Avaliação por Competências",
questoes: [/* ... */]
}
]
}
};
Distribuir para Turma:
await distribuirAvaliacao({
formularioId,
turmaId,
destinatarios: ['aluno1', 'aluno2'],
dataLimite: '2025-11-30'
});
Para Alunos
Visualizar Avaliações:
- Aba "Avaliações Periódicas"
- Status: Pendente, Iniciada, Concluída, Vencida
- Prazo limite destacado
Responder Avaliação:
- Formulário multi-página
- Salvamento automático
- Validação de campos obrigatórios
- Confirmação de envio
Estrutura do Formulário
Página 1: Informações Iniciais
- Seleção de aluno (professores/monitores)
- Nome do paciente
- Idade do paciente
- Configuração de turnos (manhã/tarde)
- Anamnese
- Exame físico
- Procedimentos
- Finalização
Página 2: Questões de Avaliação
- Informações do estudante
- Pontuação por competência (1-5)
- Pontos fortes observados
- Pontos a melhorar
- Justificativas para notas baixas
4. Gestão de Membros
Arquivo: src/pages/MembrosPage.tsx
** Funcionalidades
- Adicionar alunos por código de acesso
- Promover aluno a monitor
- Remover membros da turma
- Visualizar perfil completo
- Histórico de avaliações do aluno
🔒 SEGURANÇA IMPLEMENTADA
Correções Críticas de Segurança
1. Eliminação de Fallback Inseguro
Problema Identificado:
// ❌ PERIGOSO - Permitia escalação de privilégios
categoria: 'professor' // Fallback anterior
Solução Implementada:
// ✅ SEGURO - Sempre usa menor privilégio
categoria: 'outro' // Será bloqueado automaticamente
Arquivos Corrigidos:
src/contexts/AuthProviderProduction.tsxsrc/contexts/AuthProviderFinal.tsxsrc/contexts/AuthProviderEmergency.tsx
2. Sistema de Validação Robusto
Arquivo: src/lib/roleValidation.ts
export function sanitizeRole(role: any): UserRole {
const validRoles = ['admin', 'professor', 'monitor', 'aluno'];
if (!role || typeof role !== 'string') {
return 'outro';
}
return validRoles.includes(role) ? role : 'outro';
}
export function isRoleChangeValid(
oldRole: UserRole,
newRole: UserRole
): boolean {
// Previne escalações não autorizadas
const roleHierarchy = {
'aluno': 0,
'monitor': 1,
'professor': 2,
'admin': 3,
'outro': -1
};
return roleHierarchy[newRole] <= roleHierarchy[oldRole];
}
3. Log de Auditoria
export function logRoleChange(
userId: string,
oldRole: UserRole,
newRole: UserRole,
context: string
): void {
console.log(`[AUDIT] Mudança de papel:`, {
userId,
oldRole,
newRole,
context,
timestamp: new Date().toISOString(),
valid: isRoleChangeValid(oldRole, newRole)
});
if (!isRoleChangeValid(oldRole, newRole)) {
console.error(`[SECURITY] Escalação não autorizada detectada!`);
}
}
Proteções em Tempo Real
useRoleMonitor Hook
export function useRoleMonitor() {
const { user } = useAuth();
const [previousRole, setPreviousRole] = useState<UserRole | null>(null);
useEffect(() => {
if (user?.categoria && previousRole) {
if (user.categoria !== previousRole) {
logRoleChange(
user.id,
previousRole,
user.categoria,
'runtime-change'
);
if (!isRoleChangeValid(previousRole, user.categoria)) {
alert('Mudança de permissão detectada. Por segurança, você será desconectado.');
logout();
}
}
}
setPreviousRole(user?.categoria || null);
}, [user?.categoria]);
}
Políticas de RLS para Tabelas
Todas as tabelas implementam Row Level Security (RLS):
-- Exemplo: Turmas
CREATE POLICY "Users can only see relevant turmas"
ON turmas FOR SELECT
USING (
-- Admins veem todas
(SELECT categoria FROM profiles WHERE id = auth.uid()) = 'admin'
OR
-- Professores veem suas turmas
professor_id = auth.uid()
OR
-- Alunos veem turmas matriculadas
id IN (
SELECT turma_id FROM turma_membros
WHERE aluno_id = auth.uid()
)
);
Validação de Input
Frontend
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
nome: z.string().min(3),
categoria: z.enum(['aluno', 'professor', 'monitor', 'admin'])
});
Backend (Supabase Functions)
CHECK (categoria IN ('aluno', 'professor', 'admin', 'monitor', 'outro'))
Proteção CSRF
- Tokens JWT com expiração
- Refresh token automático
- SameSite cookies
- CORS configurado
📖 GUIA DE REPLICAÇÃO
Passo 1: Preparar Infraestrutura
Opção A: Supabase Cloud (Mais Fácil)
- Criar conta em supabase.com
- Criar novo projeto
- Anotar credenciais (URL e anon key)
Opção B: Supabase Auto-hospedado (VPS)
Requisitos:
- VPS com 2GB+ RAM
- Ubuntu 20.04+ ou Debian 11+
- Docker e Docker Compose
- Domínio apontado para VPS
Instalação:
# Clonar Supabase
git clone --depth 1 https://github.com/supabase/supabase
cd supabase/docker
# Configurar .env
cp .env.example .env
nano .env
# Iniciar serviços
docker-compose up -d
Configurar Apache Reverse Proxy:
<VirtualHost *:443>
ServerName api.seudominio.com.br
ProxyPass / http://localhost:8000/
ProxyPassReverse / http://localhost:8000/
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/seudominio.com.br/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/seudominio.com.br/privkey.pem
</VirtualHost>
Passo 2: Configurar Banco de Dados
🔴 IMPORTANTE: Execute os scripts na ordem correta!
Ordem de Execução (via SQL Editor do Supabase):
# 1. 🔴 CRÍTICO - Trigger de profile (OBRIGATÓRIO)
sql/create_profile_trigger.sql
# 2. Estrutura base
sql/avaliacoes_periodicas.sql
# 3. Configurações de usuário
sql/create_user_configs.sql
# 4. Funções auxiliares
sql/avaliacoes_periodicas_funcoes.sql
Ou executar tudo de uma vez:
sql/executar_tudo.sql
⚠️ Se encontrar erro "Database error saving new user":
Execute imediatamente:
sql/FIX_URGENTE_profile_trigger.sql
Passo 3: Clonar e Configurar Frontend
# Clonar repositório
git clone https://github.com/julioventura/evolua.git
cd evolua
# Instalar dependências
npm install
# Configurar ambiente
cp .env.example .env
nano .env
.env:
VITE_SUPABASE_URL=https://api.seudominio.com.br
VITE_SUPABASE_ANON_KEY=sua_chave_aqui
Passo 4: Build e Deploy
Desenvolvimento Local
npm run dev
Build para Produção
npm run build
Deploy no Apache
# Copiar build para Apache
sudo cp -r dist/* /var/www/html/evolua/
# Configurar VirtualHost
sudo nano /etc/apache2/sites-available/evolua.conf
evolua.conf:
<VirtualHost *:443>
ServerName cirurgia.com.br
DocumentRoot /var/www/html/evolua
<Directory /var/www/html/evolua>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
# SPA Routing
RewriteEngine On
RewriteBase /evolua/
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /evolua/index.html [L]
</Directory>
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/cirurgia.com.br/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/cirurgia.com.br/privkey.pem
</VirtualHost>
# Ativar site
sudo a2ensite evolua
sudo a2enmod rewrite
sudo systemctl reload apache2
Passo 5: Configurar N8N (Opcional)
# Instalar N8N
npm install n8n -g
# Ou via Docker
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n
Workflows Úteis:
- Notificações de novas avaliações (email/WhatsApp)
- Lembretes de prazos
- Relatórios automáticos semanais
- Backup automático do banco
Passo 6: Criar Primeiro Usuário Admin
-- No Supabase SQL Editor
-- 1. Registrar usuário via interface
-- 2. Promover para admin:
UPDATE profiles
SET categoria = 'admin'
WHERE email = 'seu@email.com';
Passo 7: Testar Sistema
Checklist:
- Login funciona
- Dashboard carrega estatísticas
- Pode criar turma
- Pode criar avaliação
- Pode adicionar alunos
- Avaliações são distribuídas
- Aluno consegue responder
- Estatísticas atualizam
🔧 RESOLUÇÃO DE PROBLEMAS
Problema: "Database error saving new user" ao Cadastrar
Sintomas:
- Erro 500 ao tentar criar conta
- Mensagem: "Database error saving new user"
- Console mostra:
Failed to load resource: the server responded with a status of 500
Causa:
Trigger de auto-criação de profile não existe, está quebrado, ou há triggers duplicados/conflitantes no banco de dados.
Diagnóstico - Verificar triggers existentes:
-- Ver TODOS os triggers na tabela auth.users
-- IMPORTANTE: Deve haver apenas 1 trigger para criar profile!
SELECT
trigger_name,
event_manipulation,
action_statement
FROM information_schema.triggers
WHERE event_object_schema = 'auth'
AND event_object_table = 'users';
Se houver mais de 1 trigger, remova os duplicados:
-- Remover triggers conflitantes (manter apenas on_auth_user_created)
DROP TRIGGER IF EXISTS on_auth_user_created_safe ON auth.users;
DROP TRIGGER IF EXISTS create_user_config_on_signup ON auth.users;
-- Adicione outros triggers duplicados se existirem
-- Verificar se só sobrou o correto
SELECT trigger_name
FROM information_schema.triggers
WHERE event_object_schema = 'auth'
AND event_object_table = 'users';
Solução Rápida (se não existir trigger):
- Acesse o Supabase Dashboard → SQL Editor
- Execute o script:
sql/FIX_URGENTE_profile_trigger.sql - Verifique se aparece "Trigger criado com sucesso!"
- Tente criar usuário novamente
Ou execute manualmente:
-- Copie e cole este código no SQL Editor do Supabase
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users CASCADE;
DROP FUNCTION IF EXISTS public.evolua_handle_new_user() CASCADE;
CREATE OR REPLACE FUNCTION public.evolua_handle_new_user()
RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER
SET search_path = public AS $$
DECLARE
v_nome TEXT;
v_categoria TEXT;
BEGIN
v_nome := COALESCE(
NEW.raw_user_meta_data->>'nome',
NEW.raw_user_meta_data->>'full_name',
split_part(NEW.email, '@', 1)
);
v_categoria := COALESCE(
NEW.raw_user_meta_data->>'categoria',
'aluno'
);
INSERT INTO public.profiles (id, nome, email, categoria, created_at, updated_at)
VALUES (NEW.id, v_nome, NEW.email, v_categoria, NOW(), NOW())
ON CONFLICT (id) DO UPDATE SET
nome = EXCLUDED.nome, email = EXCLUDED.email, updated_at = NOW();
RETURN NEW;
EXCEPTION
WHEN OTHERS THEN
RAISE WARNING 'Erro ao criar profile: % - %', SQLSTATE, SQLERRM;
RETURN NEW;
END;
$$;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE FUNCTION public.evolua_handle_new_user();
Verificar se funcionou:
SELECT trigger_name, event_object_table
FROM information_schema.triggers
WHERE trigger_name = 'on_auth_user_created';
Problema: "Verificando autenticação..." Infinito
Sintomas:
- App fica em loading infinito
- Login não completa
- Console mostra timeouts
Solução:
- Verificar conexão Supabase:
// No console do navegador
const { data, error } = await supabase
.from('profiles')
.select('*')
.limit(1);
console.log(data, error);
2. Limpar cache e storage:
3. Limpar cache e storage:
localStorage.clear();
sessionStorage.clear();
location.reload();
3. Verificar logs do AuthProvider:
4. Verificar logs do AuthProvider:
🎭 AuthProvider useEffect INICIANDO...
🎯 AuthProvider - Init simples
🔄 Auth mudou: SIGNED_IN [email]
✅ Perfil carregado
🏁 Loading finalizado
4. Se persistir, usar AuthProvider alternativo:
// Em src/contexts/AuthContext.tsx
export { AuthProvider } from './AuthProviderWorking';
Problema: ERR_NAME_NOT_RESOLVED
Causa: Variáveis de ambiente não configuradas
Solução:
- Verificar se
.envexiste - Verificar valores:
VITE_SUPABASE_URL=https://... (sem barra no final)
VITE_SUPABASE_ANON_KEY=eyJ... (chave completa)
- Reiniciar servidor dev:
# Ctrl+C para parar
npm run dev
Problema: Usuário com Categoria "outro"
Causa: Falha no carregamento de perfil
Solução Automática:
- Sistema detecta e força logout
- Mensagem aparece no login
- Verificar RLS policies no Supabase
Solução Manual:
-- Corrigir categoria no banco
UPDATE profiles
SET categoria = 'aluno' -- ou professor, admin, monitor
WHERE email = 'usuario@email.com';
Problema: Avaliações Não Aparecem
Checklist:
Verificar se usuário está na turma:
SELECT * FROM turma_membros WHERE aluno_id = 'user_id';
Verificar distribuição:
SELECT * FROM instancias_avaliacao
WHERE aluno_id = 'user_id'
ORDER BY created_at DESC;
Verificar RLS:
-- Testar query diretamente
SELECT * FROM instancias_avaliacao WHERE aluno_id = auth.uid();
Problema: Dashboard Vazio
Causa: Queries falhando silenciosamente
Solução:
- Abrir DevTools (F12)
- Procurar erros no console
- Verificar Network tab para erros 401/403
- Testar queries manualmente no Supabase
Problema: Build Falha
Erros Comuns:
TypeScript errors
# Limpar cache TypeScript
rm -rf node_modules/.cache
rm -rf dist
npm run build
Vite errors
# Reinstalar dependências
rm -rf node_modules
rm package-lock.json
npm install
Memory errors
# Aumentar limite Node
NODE_OPTIONS=--max_old_space_size=4096 npm run build
Logs de Diagnóstico
Habilitar logs detalhados
// Em src/lib/supabaseClient.ts
export const supabase = createClient(url, key, {
auth: {
persistSession: true,
autoRefreshToken: true,
detectSessionInUrl: true,
debug: true // <- Adicionar esta linha
}
});
Verificar saúde do sistema
// Executar no console
async function diagnostico() {
console.log('🔍 Iniciando diagnóstico...');
// 1. Supabase conectado?
const { data: health } = await supabase
.from('profiles')
.select('id')
.limit(1);
console.log('✅ Supabase:', health ? 'OK' : 'ERRO');
// 2. Usuário autenticado?
const { data: { user } } = await supabase.auth.getUser();
console.log('✅ Usuário:', user?.email || 'Não autenticado');
// 3. Profile carregado?
if (user) {
const { data: profile } = await supabase
.from('profiles')
.select('*')
.eq('id', user.id)
.single();
console.log('✅ Profile:', profile);
}
}
diagnostico();
📞 SUPORTE E CONTATO
Documentação Adicional
- README.md: Guia básico de início
- SUPABASE_CONFIG.md: Configuração detalhada do Supabase
- AUTH_PROVIDER_DOC.md: Documentação completa de autenticação
- SECURITY_FIX_REPORT.md: Correções de segurança implementadas
- AVALIACAO_PERIODICA_GUIDE.md: Guia de uso das avaliações
Recursos Externos
- React: react.dev
- TypeScript: typescriptlang.org
- Vite: vitejs.dev
- Tailwind CSS: tailwindcss.com
- Supabase: supabase.com/docs
📝 CHANGELOG
Versão 1.0.0 (Outubro 2025)
Funcionalidades Implementadas:
- ✅ Sistema completo de autenticação multi-papel
- ✅ Dashboard interativo com estatísticas em tempo real
- ✅ Gestão de turmas com códigos de acesso
- ✅ Sistema de avaliações periódicas
- ✅ Distribuição automática de avaliações
- ✅ Interface responsiva e moderna
- ✅ Segurança robusta com RLS
- ✅ Cache multi-camadas
- ✅ Retry logic com backoff exponencial
- ✅ Monitoramento de papéis em tempo real
- ✅ Validação automática de usuários
- ✅ Logs de auditoria completos
Correções de Segurança:
- ✅ Eliminado fallback inseguro "professor"
- ✅ Implementado sistema de validação robusto
- ✅ Prevenção de escalação de privilégios
- ✅ Proteção contra categoria "outro"
- ✅ Log de auditoria de mudanças de papel
🎯 PRÓXIMOS PASSOS
Melhorias Planejadas
- Modo offline com sincronização
- Notificações push
- Exportação de relatórios em PDF
- Dashboard analytics avançado
- Integração com Google Classroom
- App mobile (React Native)
- Sistema de badges e gamificação
- Vídeo-chamadas integradas
- Editor de formulários visual
- Cria modal de envio de mensagens de whatsapp via Servidor de Whatsapp (e não Whatsapp Web)
- Destacar "Período deve estar no formato AAAA.S (ex: 2025.1, 2025-2, ou apenas 2025)" na criação
- Ativar/Desativar turma
Desenvolvido com:
- 💙 React + TypeScript
- ⚡ Vite
- 🎨 Tailwind CSS
- 🗄️ Supabase
- 🤖 GitHub Copilot (Claude & GPT)
Licença: Proprietário
Autor: Equipe e-volua
Última Atualização: Outubro 2025
Related Documents
Valet V1 — Architecture & Implementation Plan
1. [Vision & Scope](#1-vision--scope)
Spotipy Types - Implementation Plan
A standalone type stub package for spotipy using Pydantic models generated from the official Spotify Web API OpenAPI schema.
Writing Effective Skills
What makes a skill actually work vs. being ignored or misapplied. Based on studying production skills across Claude Code (Superpowers, Trail of Bits, Anthropic's official plugins), Codex (babysit-pr, skill-creator, curated catalog), OpenClaw (55 bundled skills, 13,700+ community), and Cursor/Cline rule systems (BMAD-METHOD, RIPER-5, steipete/agent-rules).
AutoDoc Demo: Step-by-Step Walkthrough
> **Quick setup?** See the [main README](./README.md) - it takes 2 minutes.