Auditoria: Prevenção de Perda Silenciosa de Dados
Status: Fase 1 concluída (hardening de Edge Functions)
Data: 2026-03-03
Próxima fase: Auditoria completa de UX de erros + padronização de logs (pré-Release 1.0.0)
1. Resumo Executivo
Esta auditoria identificou e corrigiu 7 pontos críticos em Edge Functions onde operações de escrita (INSERT/UPDATE/DELETE) não verificavam o retorno de erro do banco de dados. Isso resultava em:
- Perda silenciosa de dados: o backend retornava
success: truemesmo quando registros não foram persistidos. - Estados inconsistentes: usuários sem papéis, escolas sem assinaturas, endereços não salvos.
- Falta de observabilidade: administradores não tinham visibilidade sobre falhas parciais no onboarding.
2. Pontos Corrigidos
2.1 especialista-olimpiadas — preparar_nova_edicao (CRÍTICO)
| Item | Detalhe |
|---|---|
| Problema | Inserts de fases_olimpiada e atividades_cronograma copiados para nova edição sem { error } check |
| Impacto | Edição criada vazia — sem fases nem atividades, sem aviso ao especialista |
| Correção | Error check + validação de contagem real de registros persistidos vs. esperados |
| Comportamento | success: true com campo warning se cópia falhou; edição não é barrada |
2.2 cadastro-escola-publica — trial_create (MÉDIO)
| Item | Detalhe |
|---|---|
| Problema | Insert de escola_assinaturas (trial) e usuario_papeis sem check |
| Impacto | Escola criada sem assinatura trial (não consegue logar) ou usuário sem papel |
| Correção | Error check + array avisos[] retornado na resposta |
| Comportamento | Cadastro não é barrado; admin vê avisos no onboarding |
2.3 cadastro-escola-publica — complete_signup (MÉDIO)
| Item | Detalhe |
|---|---|
| Problema | 5 operações sem check: endereco_escola (2x), usuario_papeis, cadastro_tokens, notificacoes |
| Impacto | Endereço perdido, papel faltando, token não invalidado, admin sem notificação |
| Correção | Todos recebem error check; erros acumulados em avisos[] |
| Comportamento | Cadastro não é barrado; avisos retornados para visibilidade |
2.4 escola-dados — update (MÉDIO)
| Item | Detalhe |
|---|---|
| Problema | Upsert de endereco_escola sem check |
| Impacto | Admin/gestor salva escola e recebe "sucesso" mesmo se endereço não persistiu |
| Correção | Error check com warning na resposta |
| Comportamento | Dados da escola salvos; warning se endereço falhou |
2.5 gestao-usuarios-escola — update (MÉDIO)
| Item | Detalhe |
|---|---|
| Problema | Delete de papéis antigos + insert do novo sem proteção; se insert falha, usuário fica sem papel |
| Impacto | Usuário perde acesso ao sistema (sem papel ativo) |
| Correção | Rollback defensivo: se insert falha, tenta re-inserir papel anterior |
| Comportamento | success: false com mensagem clara; papel anterior restaurado se possível |
2.6 admin-usuarios — update (BAIXO)
| Item | Detalhe |
|---|---|
| Problema | Desativar vínculos + inserir novos sem check individual |
| Impacto | Vínculos inconsistentes entre usuario_papeis e escola |
| Correção | Error check granular; acumula erros e retorna lista de vínculos que falharam |
| Comportamento | success: false se qualquer vínculo crítico falhou; campo legado escola_id apenas logado |
2.7 gestao-alunos — hard_delete (BAIXO)
| Item | Detalhe |
|---|---|
| Problema | Delete de aluno_responsaveis sem check; falha causa FK error genérico no delete do aluno |
| Impacto | Usuário vê erro técnico sem orientação |
| Correção | Error check com mensagem específica e orientação ao usuário |
| Comportamento | Mensagem: "Erro ao desvincular responsáveis. Tente novamente ou contate o suporte." |
3. Padrão de Error Handling Adotado
3.1 Princípio Central
Toda operação de escrita DEVE verificar
{ error }e decidir explicitamente: barrar ou avisar.
3.2 Classificação de Operações
| Tipo | Operações | Comportamento em erro |
|---|---|---|
| Crítica | Criação de usuário, atribuição de papel, criação de escola | Barra a operação e retorna success: false |
| Crítica com rollback | Troca de papel (delete + insert) | Rollback defensivo — restaura estado anterior |
| Complementar | Endereço, notificações, assinatura trial, token | Não barra — acumula em avisos[] e loga |
3.3 Template de Implementação
// ✅ Operação crítica — barra
const { error: papelError } = await supabase
.from("usuario_papeis")
.insert({ usuario_id, papel, escola_id });
if (papelError) {
console.error("[create] Erro ao vincular papel:", papelError);
return new Response(
JSON.stringify({ success: false, message: "Erro ao atribuir perfil ao usuário." }),
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
);
}
// ✅ Operação complementar — avisa
const avisos: string[] = [];
const { error: enderecoError } = await supabase
.from("endereco_escola")
.upsert({ escola_id, ...endereco });
if (enderecoError) {
console.error("[update] Erro ao salvar endereço:", enderecoError);
avisos.push("Endereço não foi salvo. Verifique os dados e tente novamente.");
}
return new Response(
JSON.stringify({
success: true,
data: resultado,
...(avisos.length > 0 && { avisos }),
}),
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
);3.4 Template de Rollback Defensivo
// ✅ Troca de papel com rollback
const papelAnterior = { ...registroAtual };
const { error: deleteError } = await supabase
.from("usuario_papeis")
.delete()
.eq("usuario_id", id);
const { error: insertError } = await supabase
.from("usuario_papeis")
.insert(novoPapel);
if (insertError) {
console.error("[update] Erro ao inserir novo papel, tentando rollback:", insertError);
const { error: rollbackError } = await supabase
.from("usuario_papeis")
.insert(papelAnterior);
if (rollbackError) {
console.error("[update] CRÍTICO: Rollback falhou:", rollbackError);
return res(false, "Erro grave ao atualizar perfil. Contate o suporte.", 500);
}
return res(false, "Erro ao trocar perfil. O perfil anterior foi mantido.", 500);
}4. Pendências para MVP 1.0.0
4.1 Auditoria de UX de Erros (Toasts)
Objetivo: Eliminar mensagens técnicas ou genéricas nos toasts do frontend.
Estado atual:
- Existe
src/lib/error-helpers.tscomgetUserFriendlyError()para traduzir erros técnicos. - Problema: Nem todos os hooks/componentes usam esse helper. Muitos exibem
error.messagediretamente ou mensagens genéricas como "Erro ao salvar".
Ação necessária:
- Auditar cada hook em
src/hooks/e cada componente comolpToast.error(). - Garantir que todas as chamadas passem por
getUserFriendlyError()ou retornem mensagens contextuais do backend. - Priorizar: mensagens que orientam o usuário sobre o que fazer (retry, verificar dados, contatar suporte).
Formato alvo de toast:
Título: "Erro ao [ação]"
Descrição: "[O que aconteceu] + [O que fazer]"Exemplos:
- ❌
"Erro: duplicate key value violates unique constraint" - ✅
"Já existe uma turma com esta combinação de série e identificador." - ❌
"Erro ao salvar" - ✅
"Não foi possível salvar o endereço da escola. Tente novamente em instantes."
4.2 Padronização de Logs (registrarLog)
Objetivo: Tornar os logs de logs_transacoes legíveis para gestores de escola (que têm acesso) e úteis para suporte técnico.
Estado atual:
- Campos
acaoseguem o formatomodulo.operacao(ex:escola.create,usuario.update). - Campo
detalhes(JSONB) varia em estrutura entre funções. - Algumas ações têm
resumoinformativo; outras têm apenas IDs técnicos.
Padrão alvo para detalhes:
{
"tipo": "create | update | soft_delete | hard_delete",
"entidade": "nome_legivel",
"entidadeId": "uuid",
"resumo": {
"campo_principal": "valor legível"
},
"descricao": "Frase curta descrevendo o que aconteceu para exibição na UI de logs",
"alteracoes": [
{ "campo": "nome_legivel", "antes": "X", "depois": "Y" }
]
}Campo descricao (novo — prioridade para MVP):
- Frase em português, legível por não-técnicos.
- Exemplos:
"Turma 6A criada para o 6º ano do Ensino Fundamental.""Perfil de João Silva atualizado: papel alterado de Coordenador para Diretor.""Aluno Maria Souza excluído permanentemente do sistema."
Ação necessária:
- Auditar todas as chamadas a
registrarLog()em todas as Edge Functions. - Adicionar campo
descricaolegível em cada uma. - Padronizar o
resumocom campos que façam sentido para o gestor.
4.3 Auditoria Tela a Tela
Objetivo: Verificar que cada tela (incluindo abas e modais) tem tratamento de erro completo em todos os fluxos de escrita.
Metodologia proposta:
- Mapear cada tela → hooks utilizados → Edge Functions chamadas → ações executadas.
- Para cada ação de escrita: verificar se o hook trata erro, se o toast é amigável, se o log é descritivo.
- Documentar gaps em uma tabela de tracking.
Tabela modelo:
| Tela | Aba | Hook | Edge Function | Ação | Toast OK? | Log OK? | Status |
|---|---|---|---|---|---|---|---|
| Turmas | Lista | useGestaoTurmas | gestao-turmas | create | ⚠️ | ⚠️ | Pendente |
| Alunos | Importação | useImportacaoSessao | gestao-alunos | batch_create | ✅ | ⚠️ | Parcial |
Esta tabela será preenchida durante a auditoria pré-release.
5. Checklist Pré-Release 1.0.0
## Qualidade & Observabilidade
- [x] Error handling em inserts/updates de Edge Functions (esta auditoria)
- [ ] Auditoria de toasts — eliminar mensagens técnicas (§4.1)
- [ ] Padronização de `registrarLog` com `descricao` legível (§4.2)
- [ ] Auditoria tela a tela de fluxos de escrita (§4.3)
## Segurança
- [ ] Validação de CPF/CNPJ (Fase C do roadmap)
- [ ] Auditoria do endpoint `/me` (Fase D)
- [ ] CAPTCHA + Rate Limit por IP (Fase E)
## Entrega
- [ ] Tag de release v1.0.0 no GitHub
- [ ] Changelog documentado
- [ ] Processo de release notes para novas features6. Arquivos Corrigidos nesta Auditoria
| Arquivo | Correções |
|---|---|
supabase/functions/especialista-olimpiadas/index.ts | Error check em preparar_nova_edicao (fases + atividades) |
supabase/functions/cadastro-escola-publica/index.ts | Error checks em trial_create (assinatura + papel) e complete_signup (endereço + papel + token + notificações) |
supabase/functions/escola-dados/index.ts | Error check em update (endereço) |
supabase/functions/gestao-usuarios-escola/index.ts | Rollback defensivo em update (troca de papel) |
supabase/functions/admin-usuarios/index.ts | Error checks granulares em update (vínculos de papel) |
supabase/functions/gestao-alunos/index.ts | Error check em hard_delete (desvínculo de responsáveis) |
Documento mantido em docs/security/SILENT_DATA_LOSS_AUDIT.md — atualizar conforme novas correções forem aplicadas.