Skip to content

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 UploadedFile no 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

Sistema de Gestão de Arquivos Digitais