Skip to content

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: true mesmo 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-olimpiadaspreparar_nova_edicao (CRÍTICO)

ItemDetalhe
ProblemaInserts de fases_olimpiada e atividades_cronograma copiados para nova edição sem { error } check
ImpactoEdição criada vazia — sem fases nem atividades, sem aviso ao especialista
CorreçãoError check + validação de contagem real de registros persistidos vs. esperados
Comportamentosuccess: true com campo warning se cópia falhou; edição não é barrada

2.2 cadastro-escola-publicatrial_create (MÉDIO)

ItemDetalhe
ProblemaInsert de escola_assinaturas (trial) e usuario_papeis sem check
ImpactoEscola criada sem assinatura trial (não consegue logar) ou usuário sem papel
CorreçãoError check + array avisos[] retornado na resposta
ComportamentoCadastro não é barrado; admin vê avisos no onboarding

2.3 cadastro-escola-publicacomplete_signup (MÉDIO)

ItemDetalhe
Problema5 operações sem check: endereco_escola (2x), usuario_papeis, cadastro_tokens, notificacoes
ImpactoEndereço perdido, papel faltando, token não invalidado, admin sem notificação
CorreçãoTodos recebem error check; erros acumulados em avisos[]
ComportamentoCadastro não é barrado; avisos retornados para visibilidade

2.4 escola-dadosupdate (MÉDIO)

ItemDetalhe
ProblemaUpsert de endereco_escola sem check
ImpactoAdmin/gestor salva escola e recebe "sucesso" mesmo se endereço não persistiu
CorreçãoError check com warning na resposta
ComportamentoDados da escola salvos; warning se endereço falhou

2.5 gestao-usuarios-escolaupdate (MÉDIO)

ItemDetalhe
ProblemaDelete de papéis antigos + insert do novo sem proteção; se insert falha, usuário fica sem papel
ImpactoUsuário perde acesso ao sistema (sem papel ativo)
CorreçãoRollback defensivo: se insert falha, tenta re-inserir papel anterior
Comportamentosuccess: false com mensagem clara; papel anterior restaurado se possível

2.6 admin-usuariosupdate (BAIXO)

ItemDetalhe
ProblemaDesativar vínculos + inserir novos sem check individual
ImpactoVínculos inconsistentes entre usuario_papeis e escola
CorreçãoError check granular; acumula erros e retorna lista de vínculos que falharam
Comportamentosuccess: false se qualquer vínculo crítico falhou; campo legado escola_id apenas logado

2.7 gestao-alunoshard_delete (BAIXO)

ItemDetalhe
ProblemaDelete de aluno_responsaveis sem check; falha causa FK error genérico no delete do aluno
ImpactoUsuário vê erro técnico sem orientação
CorreçãoError check com mensagem específica e orientação ao usuário
ComportamentoMensagem: "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

TipoOperaçõesComportamento em erro
CríticaCriação de usuário, atribuição de papel, criação de escolaBarra a operação e retorna success: false
Crítica com rollbackTroca de papel (delete + insert)Rollback defensivo — restaura estado anterior
ComplementarEndereço, notificações, assinatura trial, tokenNão barra — acumula em avisos[] e loga

3.3 Template de Implementação

typescript
// ✅ 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

typescript
// ✅ 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.ts com getUserFriendlyError() para traduzir erros técnicos.
  • Problema: Nem todos os hooks/componentes usam esse helper. Muitos exibem error.message diretamente ou mensagens genéricas como "Erro ao salvar".

Ação necessária:

  • Auditar cada hook em src/hooks/ e cada componente com olpToast.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 acao seguem o formato modulo.operacao (ex: escola.create, usuario.update).
  • Campo detalhes (JSONB) varia em estrutura entre funções.
  • Algumas ações têm resumo informativo; outras têm apenas IDs técnicos.

Padrão alvo para detalhes:

json
{
  "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 descricao legível em cada uma.
  • Padronizar o resumo com 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:

  1. Mapear cada tela → hooks utilizados → Edge Functions chamadas → ações executadas.
  2. Para cada ação de escrita: verificar se o hook trata erro, se o toast é amigável, se o log é descritivo.
  3. Documentar gaps em uma tabela de tracking.

Tabela modelo:

TelaAbaHookEdge FunctionAçãoToast OK?Log OK?Status
TurmasListauseGestaoTurmasgestao-turmascreate⚠️⚠️Pendente
AlunosImportaçãouseImportacaoSessaogestao-alunosbatch_create⚠️Parcial

Esta tabela será preenchida durante a auditoria pré-release.


5. Checklist Pré-Release 1.0.0

markdown
## 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 features

6. Arquivos Corrigidos nesta Auditoria

ArquivoCorreções
supabase/functions/especialista-olimpiadas/index.tsError check em preparar_nova_edicao (fases + atividades)
supabase/functions/cadastro-escola-publica/index.tsError checks em trial_create (assinatura + papel) e complete_signup (endereço + papel + token + notificações)
supabase/functions/escola-dados/index.tsError check em update (endereço)
supabase/functions/gestao-usuarios-escola/index.tsRollback defensivo em update (troca de papel)
supabase/functions/admin-usuarios/index.tsError checks granulares em update (vínculos de papel)
supabase/functions/gestao-alunos/index.tsError 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.