API de Upload
Endpoints para upload de PDFs e gerenciamento de arquivos.
upload.requestUploadUrl
Requisita URL presigned para upload direto para R2.
Tipo: Mutation (POST)
Autenticação: Obrigatória
Input
typescript
{
fileName: string; // Deve terminar com .pdf
path?: string; // Opcional, auto-gerado se vazio
}Response
typescript
{
uploadUrl: string; // Presigned URL (validade: 15 min)
fileKey: string; // Chave R2 do arquivo
id: string; // UUID do registro de upload
path: string; // Path slugificado
fileName: string; // Nome sanitizado
}Exemplo
typescript
const request = trpc.upload.requestUploadUrl.useMutation();
const { uploadUrl, fileKey, id, fileName } = await request.mutateAsync({
fileName: "relatorio-anual.pdf"
});
// Upload direto para R2
await axios.put(uploadUrl, file, {
headers: { "Content-Type": "application/pdf" }
});upload.confirmUpload
Confirma que upload foi realizado com sucesso.
Tipo: Mutation (POST)
Autenticação: Obrigatória
Input
typescript
{
id: string; // UUID do registro
path: string; // Path slugificado
fileKey: string; // Chave R2
fileName: string; // Nome sanitizado
fileSize?: number; // Tamanho em bytes (opcional)
}Response
typescript
{
success: true;
uploadedFileId: string;
}Efeitos Colaterais
- Cria registro
UploadedFileno banco - Enfileira job de processamento
PROCESS_PDF - Inicia extração e conversão de páginas em background
upload.getPdfPages
Consulta status de processamento e páginas extraídas.
Tipo: Query (GET)
Autenticação: Não requerida
Input
typescript
{
fileId: string; // UUID do UploadedFile
}Response
typescript
{
id: string;
fileName: string;
processingStatus: "PENDING" | "GENERATING_IMAGES" | "CONVERTING_IMAGES" | "COMPLETED" | "FAILED";
totalPages: number | null;
pages: Array<{
id: string;
pageNumber: number;
r2FileKey: string;
fileName: string;
fileSize: bigint | null;
}>;
}Status de Processamento
- PENDING: Aguardando processamento
- GENERATING_IMAGES: Extraindo páginas (pdftoppm)
- CONVERTING_IMAGES: Convertendo PNG → WebP
- COMPLETED: Todas as páginas processadas
- FAILED: Erro no processamento
upload.getFilesTree
Retorna árvore hierárquica de todos os arquivos.
Tipo: Query (GET)
Autenticação: Não requerida
Response
typescript
{
[year: string]: {
[month: string]: {
[day: string]: {
[batchId: string]: Array<{
id: string;
fileName: string;
fileType: "PDF" | "IMAGE";
fileSize: bigint;
r2FileKey: string;
processingStatus?: string;
pages?: Array<...>;
}>
}
}
}
}Estrutura
json
{
"2026": {
"01": {
"14": {
"relatorio-anual": [
{
"id": "uuid",
"fileName": "relatorio-anual.pdf",
"fileType": "PDF",
"fileSize": 2500000,
"processingStatus": "COMPLETED",
"pages": [...]
},
{
"id": "uuid",
"fileName": "relatorio-anual-pg-1.webp",
"fileType": "IMAGE",
"fileSize": 245000
}
]
}
}
}
}upload.getAvailableForAttachment
Lista arquivos disponíveis para anexar a documentos.
Tipo: Query (GET)
Autenticação: Não requerida
Response
typescript
Array<{
id: string;
fileName: string;
fileType: "PDF" | "IMAGE";
fileSize: bigint;
contentType: string;
processingStatus: "COMPLETED";
r2FileKey: string;
batchId: string;
uploadedAt: Date;
pages: Array<...>;
}>Filtros
- Apenas arquivos com
processingStatus: "COMPLETED" - Inclui PDFs e suas páginas extraídas
- Ordenado por data de upload (mais recente primeiro)
upload.deleteFile
Deleta arquivo do storage (apenas SUPER usuários).
Tipo: Mutation (POST)
Autenticação: Super User apenas
Input
typescript
{
fileId: string;
}Validações
- Verifica se arquivo está vinculado a documentos
- Verifica se é PDF com páginas vinculadas
- Retorna erro se arquivo em uso
Erros
typescript
// FORBIDDEN
{
"error": {
"code": "FORBIDDEN",
"message": "Apenas super usuários podem deletar arquivos"
}
}
// CONFLICT
{
"error": {
"code": "CONFLICT",
"message": "Não é possível deletar este arquivo pois está vinculado a 3 documento(s)"
}
}Fluxo Completo de Upload
typescript
// 1. Requisitar URL
const { uploadUrl, fileKey, id, fileName } =
await trpc.upload.requestUploadUrl.mutate({
fileName: file.name
});
// 2. Upload para R2
await axios.put(uploadUrl, file, {
headers: { "Content-Type": "application/pdf" }
});
// 3. Confirmar upload
await trpc.upload.confirmUpload.mutate({
id,
fileKey,
fileName,
path: slugify(file.name),
fileSize: file.size
});
// 4. Monitorar processamento (polling)
const checkStatus = setInterval(async () => {
const { processingStatus } =
await trpc.upload.getPdfPages.query({ fileId: id });
if (processingStatus === "COMPLETED") {
clearInterval(checkStatus);
toast.success("Processamento concluído!");
}
}, 5000);Limites
- Tamanho máximo: 50MB por arquivo (configurável)
- Formato: Apenas PDF
- Duplicatas: Máximo 100 por dia por nome
- Presigned URL: 15 minutos de validade
- Download URL: 1 hora de validade