Upload Inviolável: Arquitetura de Arquivos Privados com Supabase e RLS
Índice do Manual
Se a sua empresa desenvolve sistemas corporativos, como plataformas para Clínicas Médicas ou softwares contábeis, vazar um documento confidencial de um cliente é o fim da linha. O risco jurídico e a quebra de confiança costumam levar à falência.
Infelizmente, o erro mais amador e perigoso do desenvolvimento de software acontece no exato momento em que um usuário faz o upload de um documento.
A arquitetura tradicional e falha ensina o frontend a enviar o arquivo PDF para o servidor backend (como uma rota de API do Next.js), para que o backend salve no banco de dados. Isso traz três problemas catastróficos:
- Estouro de Memória: O servidor Node.js/Next.js gasta RAM excessiva segurando o arquivo.
- Limite de Payload: Servidores modernos (Serverless) na Vercel bloqueiam requisições maiores que 4.5MB.
- Falta de Isolamento: Se o link do arquivo "vazar", qualquer pessoa na internet consegue baixá-lo.
Como Especialista em Sistemas e Integrações, a regra que orquestro nos meus projetos é impiedosa e resolve as três falhas simultaneamente.
Este manual documenta a nossa arquitetura oficial de Upload Direto e Inviolável.
A Arquitetura do Cofre: Frontend direto para o Supabase Storage
A primeira regra: O servidor intermediário nunca deve tocar no arquivo.
O navegador do cliente (React) se conecta diretamente aos cofres da nuvem (Supabase Storage). O servidor web (Next.js) fica apenas assistindo, poupando 100% da sua memória e processamento.
No frontend, o código de upload fica assim:
// Componente de Upload no Frontend (React)
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('SUA_URL', 'SUA_CHAVE_PUBLICA')
async function uploadDocumento(arquivo: File, usuarioId: string) {
// A Regra de Ouro: O arquivo é salvo dentro de uma pasta com o ID exato do usuário
const caminhoArquivo = `${usuarioId}/rg-frente.pdf`;
const { data, error } = await supabase.storage
.from('documentos-privados')
.upload(caminhoArquivo, arquivo);
if (error) throw error;
return data;
}
Até aqui, nós economizamos servidores. Mas como garantimos a segurança absoluta?
Row Level Security (RLS) no Supabase Storage
O Supabase Storage não é apenas um "S3" burro da AWS. Ele é intimamente ligado ao núcleo do banco de dados PostgreSQL.
Para impedir que a Empresa A acesse os PDFs da Empresa B, nós não escrevemos condicionais (if/else) falhos no Javascript. Nós injetamos a regra de bloqueio direto na raiz matemática do banco de dados, através do Row Level Security (RLS).
Criamos um bucket (cofre) privado chamado documentos-privados e escrevemos o seguinte Script SQL no Postgres para proibir acessos não autorizados:
-- 1. Habilitamos a segurança máxima no bucket
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
-- 2. Política de INSERÇÃO (Upload)
-- Apenas usuários logados podem enviar arquivos. E eles só podem salvar
-- arquivos dentro da pasta que leva o seu próprio ID de usuário.
CREATE POLICY "Usuário só faz upload na própria pasta"
ON storage.objects FOR INSERT TO authenticated
WITH CHECK (
bucket_id = 'documentos-privados' AND
(storage.foldername(name))[1] = auth.uid()::text
);
-- 3. Política de LEITURA (Download)
-- Um usuário só consegue visualizar e baixar o documento se o nome
-- da pasta onde ele está guardado for exatamente igual ao ID do usuário logado.
CREATE POLICY "Usuário só pode ler seus próprios documentos"
ON storage.objects FOR SELECT TO authenticated
USING (
bucket_id = 'documentos-privados' AND
(storage.foldername(name))[1] = auth.uid()::text
);
A Inviolabilidade
Com esse código no banco de dados, o vazamento de dados torna-se matematicamente impossível.
Se um hacker tentar pegar o link direto do documento (https://seu-projeto.supabase.co/storage/v1/object/public/documentos-privados/ID_DE_OUTRO_USUARIO/rg.pdf), o banco de dados vai comparar a identidade da conexão ativa (auth.uid()) com o nome da pasta na URL.
Como eles não baterão, a resposta do servidor será instantânea e impiedosa: Acesso Negado (403 Forbidden). Nenhuma linha de código no seu Next.js precisou ser escrita para validar essa segurança.
Se você trata os dados dos seus clientes com amadorismo, a Lei Geral de Proteção de Dados (LGPD) vai destruir a sua margem de lucro. A arquitetura correta transfere o peso da segurança para o banco de dados.