convertor/INSTRUCOES_FORK.md

38 KiB

Objetivo

Transformar o ConvertX em um serviço backend interno, sem necessidade de UI, para suportar principalmente:

  • DOCX -> TXT
  • DOCX -> texto bruto JSON
  • PDF -> texto
  • XLSX -> texto/CSV
  • ODT -> texto
  • HTML/MD -> texto
  • PPT -> TXT
  • PPTX -> TXT
  • DOC -> TXT
  • XLS -> TXT

O uso principal será:

  • n8n baixa arquivo do Nextcloud
  • n8n envia arquivo ao serviço
  • serviço devolve texto limpo
  • n8n indexa no Supabase

────────────────────────────────────────────────────────────────────────────────

Decisão arquitetural

Não fazer

  • scraping da UI do ConvertX
  • automação de login/session/cookies
  • usar endpoints web atuais como API informal
  • expor interface pública para clientes

Fazer

Criar um fork com API interna dedicada, separada da UI.

Estratégia recomendada

Adicionar uma camada nova de rotas, por exemplo:

  • POST /api/extract-text
  • POST /api/convert
  • GET /healthz

Com autenticação simples por token interno.

────────────────────────────────────────────────────────────────────────────────

Estado atual observado

Pelo container investigado:

  • o projeto já tem motores funcionais:
  • LibreOffice
  • calibre
  • pandoc
  • markitdown
  • o projeto já faz conversões reais
  • logs confirmam conversão bem-sucedida de docx -> txt
  • há código compilado indicando:
  • src/converters/main
  • handleConvert(...)
  • conversores separados por engine
  • a aplicação atual é UI-first
  • existem rotas web, mas não uma API limpa/documentada para integração M2M

Conclusão: a parte difícil da conversão já existe. O trabalho principal é expor isso de forma limpa, previsível e segura.

────────────────────────────────────────────────────────────────────────────────

Meta funcional do fork

MVP

Entregar um serviço que aceite upload de arquivo e retorne texto extraído.

Endpoint principal

POST /api/extract-text

Input

Multipart upload:

  • file: arquivo
  • opcional:
  • filename
  • mimeType
  • preferredConverter
  • formatHint

Output

  {
  "ok": true,
  "filename": "contrato.docx",
  "detectedInputType": "docx",
  "converter": "calibre",
  "text": "texto extraído...",
  "meta": {
  "chars": 12345,
  "warnings": [],
  "durationMs": 842
  }
  }

V2

Adicionar endpoint de conversão genérica:

POST /api/convert

Input:

  • arquivo
  • target=txt|pdf|html|md

Output:

  • binário convertido ou
  • JSON com downloadUrl temporária

V3

Adicionar modos assíncronos para arquivos grandes:

  • POST /api/jobs
  • GET /api/jobs/:id
  • GET /api/jobs/:id/result

────────────────────────────────────────────────────────────────────────────────

Proposta de design técnico

  1. Criar modo “service-only”

Adicionar variável de ambiente:

  • SERVICE_MODE=true

Comportamento

Quando SERVICE_MODE=true:

  • não montar UI
  • não depender de login visual
  • subir apenas:
  • healthcheck
  • rotas de API
  • middlewares essenciais

Benefícios

  • superfície menor
  • menos complexidade
  • mais fácil de operar
  • menos risco de endpoints web desnecessários

────────────────────────────────────────────────────────────────────────────────

  1. Criar namespace /api

Separar totalmente das rotas existentes de UI.

Rotas sugeridas

  • GET /healthz
  • GET /api/healthz
  • POST /api/extract-text
  • POST /api/convert
  • opcional:
  • GET /api/formats
  • GET /api/converters

────────────────────────────────────────────────────────────────────────────────

  1. Criar camada de autenticação interna

Como é serviço de infraestrutura, não precisa auth de usuário final.

Recomendado

Auth por header estático:

  • Authorization: Bearer <SERVICE_TOKEN>

ou

  • X-API-Key: <SERVICE_TOKEN>

Variável

  • SERVICE_TOKEN=...

Regra

  • se SERVICE_MODE=true, bloquear todas as rotas /api/* sem token válido
  • exceção:
  • /healthz

Se rodar só em rede interna

Pode até começar sem auth em ambiente fechado, mas eu recomendo token desde o início.

────────────────────────────────────────────────────────────────────────────────

Arquivos/áreas prováveis para alterar no fork

Baseado no que encontrei no container:

Áreas principais

  • src/index.tsx
  • src/converters/main.ts
  • src/converters/*.ts
  • src/helpers/*
  • páginas/rotas web em src/pages/*

Novo código a criar

Novo diretório sugerido

  • src/api/

Arquivos sugeridos

  • src/api/auth.ts
  • src/api/health.ts
  • src/api/extractText.ts
  • src/api/convert.ts
  • src/api/utils/files.ts
  • src/api/utils/text.ts
  • src/api/utils/cleanup.ts

────────────────────────────────────────────────────────────────────────────────

Implementação recomendada por etapas

Etapa 1 — mapear a entrada do app atual

Objetivo: entender o mínimo necessário para chamar a conversão sem UI.

Tarefa DEV

Inspecionar:

  • como src/pages/upload.tsx salva upload
  • como src/pages/convert.tsx chama handleConvert
  • assinatura real de handleConvert(...)
  • formato esperado de:
  • upload dir
  • output dir
  • converter name
  • target format

Entregável

Documento curto:

  • fluxo atual de upload → conversão → output
  • dependências implícitas do sistema de jobs

────────────────────────────────────────────────────────────────────────────────

Etapa 2 — isolar a lógica reutilizável

Objetivo: desacoplar conversão da camada web.

Se handleConvert estiver muito acoplado

Criar função nova, por exemplo:

  convertSingleFile({
  inputPath,
  outputDir,
  target,
  preferredConverter,
  options
  })

Retornando algo como:

  {
  outputPath: string,
  converterUsed: string,
  target: string
  }

Ideal

Essa função deve:

  • não depender de cookie
  • não depender de user id
  • não depender de job id da UI
  • não depender de db/history

Entregável

Uma função reutilizável de backend puro.

────────────────────────────────────────────────────────────────────────────────

Etapa 3 — implementar POST /api/extract-text

Objetivo: MVP útil para n8n.

Fluxo interno

  1. receber multipart com arquivo
  2. salvar em diretório temporário
  3. detectar tipo:
  • por extensão
  • e/ou MIME
  1. escolher rota de extração

Regras sugeridas por tipo

  • docx
  • tentar calibre -> txt
  • fallback libreoffice -> txt
  • fallback markitdown se fizer sentido
  • odt
  • libreoffice
  • pdf
  • se houver extrator apropriado, usar
  • senão deixar fora do MVP
  • txt, md, csv
  • leitura direta
  • html
  • converter para texto via sanitização

Pós-processamento de texto

Aplicar normalização:

  • trim
  • normalizar CRLF/LF
  • remover excesso de linhas vazias
  • opcional:
  • remover caracteres nulos
  • normalizar unicode
  • colapsar whitespace sem destruir parágrafos

Output

Sempre JSON:

  {
  "ok": true,
  "text": "...",
  "converter": "calibre",
  "detectedInputType": "docx",
  "meta": {
  "chars": 1234,
  "durationMs": 820,
  "tempFilesCleaned": true
  }
  }

Erros

Padrão:

  {
  "ok": false,
  "error": {
  "code": "UNSUPPORTED_INPUT",
  "message": "Unsupported input type: xlsm"
  }
  }

────────────────────────────────────────────────────────────────────────────────

Etapa 4 — implementar escolha de conversor

Objetivo: não depender de um único backend.

Ordem recomendada para DOCX->TXT

  1. calibre
  2. libreoffice
  3. pandoc ou markitdown se útil

Por quê

Você já viu nos logs que o ConvertX converteu DOCX→TXT usando calibre com sucesso.

Interface sugerida

Criar um resolvedor:

  resolveTextExtractor(inputType, preferredConverter?)

Retorna:

  • função de conversão
  • nome do conversor

Benefício

No futuro, trocar backend sem mexer na API.

────────────────────────────────────────────────────────────────────────────────

Etapa 5 — implementar storage temporário robusto

Objetivo: evitar vazamento de arquivos e sujeira em disco.

Recomendação

Estrutura temporária por request:

  • /tmp/convertx-service//input
  • /tmp/convertx-service//output

Regras

  • gerar requestId UUID
  • limpar sempre no finally
  • logar falha de cleanup sem quebrar resposta principal
  • adicionar TTL cleanup defensivo por cron se quiser

Importante

Nunca confiar no nome do arquivo bruto do usuário para compor path.

────────────────────────────────────────────────────────────────────────────────

Etapa 6 — health e observabilidade

Endpoint

GET /healthz

Retorno:

  {
  "ok": true,
  "service": "convertx-service",
  "serviceMode": true,
  "converters": {
  "libreoffice": true,
  "calibre": true,
  "pandoc": true
  }
  }

Também recomendado

GET /api/formats Retorna:

  • entradas suportadas para extração
  • conversores disponíveis

Logging

Log por request:

  • requestId
  • filename
  • detected input type
  • converter escolhido
  • duration
  • sucesso/falha

Sem logar conteúdo do documento.

────────────────────────────────────────────────────────────────────────────────

Contrato recomendado para o n8n

Chamada HTTP Request

POST /api/extract-text

Headers

  • Authorization: Bearer <SERVICE_TOKEN>

Body

Multipart:

  • file: binário do Nextcloud

Resposta esperada no n8n

  {
  "ok": true,
  "text": "..."
  }

Integração no workflow

Branch DOCX fica:

  1. Nextcloud download
  2. HTTP Request -> convertx-service /api/extract-text
  3. Set data = {{$json.text}}
  4. Update Data RAG
  5. Move para processados

────────────────────────────────────────────────────────────────────────────────

Segurança

Requisitos mínimos

  • rodar em rede interna
  • autenticação por token
  • limite de tamanho de upload
  • tipos aceitos explicitamente
  • sanitização de nome de arquivo
  • limpeza garantida de temporários
  • timeout por conversão

Recomendado

  • MAX_UPLOAD_MB
  • REQUEST_TIMEOUT_MS
  • ALLOWED_INPUT_TYPES=docx,txt,md ,pdf,...

Não fazer

  • expor sem auth na internet
  • aceitar qualquer formato indiscriminadamente
  • armazenar documentos convertidos por padrão

────────────────────────────────────────────────────────────────────────────────

Performance e concorrência

MVP

Processamento síncrono por request está ok.

Limites recomendados

  • 1 request = 1 temp dir
  • limite de concorrência configurável
  • timeouts por engine

Variáveis sugeridas

  • MAX_CONCURRENT_CONVERSIONS=2 ou 4
  • REQUEST_TIMEOUT_MS=60000

Se ficar pesado depois

Migrar para fila/job assíncrono.

────────────────────────────────────────────────────────────────────────────────

Plano de entrega para DEV

Sprint 1 — API mínima funcional

Objetivo

Subir POST /api/extract-text para DOCX.

Itens

  • fork do projeto
  • SERVICE_MODE=true
  • GET /healthz
  • POST /api/extract-text
  • token auth
  • temp storage
  • DOCX -> TXT via calibre/libreoffice
  • retorno JSON com texto
  • logs básicos

Critério de aceite

  • enviar DOCX e receber texto limpo
  • n8n consegue consumir
  • sem UI necessária

────────────────────────────────────────────────────────────────────────────────

Sprint 2 — endurecimento

Itens

  • fallback entre conversores
  • suporte a TXT/MD/HTML direto
  • timeouts
  • limites de upload
  • limpeza robusta
  • testes automatizados
  • imagem Docker própria do fork

Critério de aceite

  • falha controlada
  • zero lixo de temp após requests
  • respostas padronizadas de erro

────────────────────────────────────────────────────────────────────────────────

Sprint 3 — extensões úteis

Itens

  • POST /api/convert
  • GET /api/formats
  • suporte a PDF/XLSX conforme prioridade
  • métricas e logs melhores
  • docs internas para integração com n8n

────────────────────────────────────────────────────────────────────────────────

Testes que DEV precisa cobrir

Testes funcionais

DOCX simples

  • texto corrido
  • múltiplos parágrafos
  • heading + lista

DOCX médio

  • tabela
  • quebra de página
  • caracteres acentuados
  • unicode

Casos de erro

  • arquivo inválido
  • ZIP corrompido
  • extensão falsa
  • arquivo muito grande
  • timeout de conversão

Testes de contrato

  • resposta JSON sempre válida
  • ok=true/false
  • text presente quando sucesso
  • error.code presente quando falha

Testes de operação

  • cleanup de temp
  • concorrência de 2-5 requests
  • comportamento com conversor ausente

────────────────────────────────────────────────────────────────────────────────

Docker/Deploy do fork

Recomendação

Imagem própria, por exemplo:

  • ghcr.io//convertx-service

Execução

Sem UI e com modo serviço:

  • SERVICE_MODE=true
  • SERVICE_TOKEN=...

Exemplo de compose conceitual

  services:
  convertx-service:
  image: ghcr.io/<org>/convertx-service:latest
  restart: unless-stopped
  ports:
  - "127.0.0.1:3010:3000"
  environment:
  - SERVICE_MODE=true
  - SERVICE_TOKEN=supersecret
  - MAX_UPLOAD_MB=50
  - REQUEST_TIMEOUT_MS=60000
  volumes:
  - ./data:/app/data

Observação

Se não precisar persistência, pode usar volume temporário. Mas algum diretório de trabalho é útil para debug inicial.

────────────────────────────────────────────────────────────────────────────────

Riscos e mitigação

Risco 1 — handleConvert muito acoplado à UI/job/db

Mitigação

Extrair função backend pura de conversão.

Risco 2 — output TXT ruim em alguns DOCX

Mitigação

Implementar fallback entre calibre/libreoffice/markitdown.

Risco 3 — vazar arquivos temporários

Mitigação

try/finally + cleanup periódico defensivo.

Risco 4 — serviço virar gargalo

Mitigação

limite de concorrência + timeout + futura fila assíncrona.

Risco 5 — quebra com updates upstream

Mitigação

manter mudanças isoladas em src/api/* e em poucos pontos do bootstrap.

────────────────────────────────────────────────────────────────────────────────

Recomendação de abordagem de código

Evitar

Modificar agressivamente o fluxo web já existente.

Preferir

Adicionar uma camada nova, quase orthogonal:

  • src/api/*
  • boot condicional no src/index.tsx

Assim o fork:

  • continua próximo do upstream
  • fica mais fácil de manter
  • reduz conflito de merge

────────────────────────────────────────────────────────────────────────────────

Definição de pronto

O fork estará “pronto” para uso no n8n quando:

  • POST /api/extract-text aceitar DOCX
  • devolver texto confiável
  • exigir token interno
  • limpar temporários
  • expor GET /healthz
  • rodar em container sem UI
  • ser consumível pelo workflow com um único HTTP Request

────────────────────────────────────────────────────────────────────────────────

Minha recomendação final ao time DEV

Build order

  1. extrair/consolidar função backend de conversão
  2. criar /api/extract-text
  3. adicionar auth + cleanup
  4. dockerizar modo service-only
  5. integrar no n8n
  6. só depois pensar em /api/convert genérico

Porque o caso de uso real hoje é: DOCX -> texto para RAG e não um conversor universal completo.

Se quiser, no próximo passo eu posso transformar isso em um artefato mais operacional para o time, por exemplo:

  • checklist técnico por tarefa
  • ADR curta
  • ou especificação de endpoints com payloads exatos.

faça os 3

Perfeito. Vou te entregar os 3 artefatos:

  1. checklist técnico por tarefa
  2. ADR curta
  3. especificação de endpoints com payloads exatos

────────────────────────────────────────────────────────────────────────────────

  1. CHECKLIST TÉCNICO POR TAREFA

Projeto: Fork do ConvertX para modo service-only interno

Objetivo do MVP

Entregar um serviço interno que faça:

  • DOCX -> texto
  • consumível por n8n via HTTP
  • sem UI
  • sem login humano
  • com token interno
  • com cleanup de arquivos temporários

────────────────────────────────────────────────────────────────────────────────

EPIC A — Preparação do fork

Tarefa A1 — Criar fork e baseline

Checklist

  • Criar fork do repositório C4illin/ConvertX
  • Definir nome do fork/produto interno
  • sugestão: convertx-service
  • Criar branch inicial:
  • feat/service-mode-api
  • Documentar hash/tag upstream base usada no fork
  • Confirmar build local do projeto original
  • Confirmar que a imagem Docker do fork sobe igual ao upstream

Entregável

  • fork funcional
  • branch de trabalho criada
  • README interno inicial

────────────────────────────────────────────────────────────────────────────────

Tarefa A2 — Mapear fluxo atual de conversão

Checklist

  • Inspecionar src/index.tsx
  • Inspecionar src/pages/upload*
  • Inspecionar src/pages/convert*
  • Inspecionar src/converters/main*
  • Localizar assinatura real de handleConvert(...)
  • Identificar dependências com:
  • user/session
  • jobId
  • sqlite/history
  • diretórios uploads/output
  • Mapear onde ocorre a seleção de conversor
  • Mapear onde o output final é salvo
  • Mapear tratamento de erro atual

Entregável

  • documento curto: “conversion flow map”
  • diagrama simples do fluxo atual

────────────────────────────────────────────────────────────────────────────────

EPIC B — Desacoplamento da lógica de conversão

Tarefa B1 — Extrair função backend pura

Objetivo Criar função reutilizável sem dependência da UI.

Checklist

  • Criar módulo novo, ex:
  • src/service/convertSingleFile.ts
  • Definir interface de entrada:
  type ConvertSingleFileInput = {
  inputPath: string
  outputDir: string
  target: string
  preferredConverter?: string
  options?: Record<string, unknown>
  }
  • Definir interface de saída:
  type ConvertSingleFileResult = {
  outputPath: string
  converterUsed: string
  target: string
  durationMs: number
  }
  • Reusar lógica de conversão existente
  • Remover dependência de:
  • cookie
  • user id
  • job id
  • páginas web
  • Garantir que a função lance erro padronizado em falha

Entregável

  • função backend pura testável

────────────────────────────────────────────────────────────────────────────────

Tarefa B2 — Extrair estratégia de “texto”

Objetivo Criar uma camada que escolha o melhor conversor para extrair texto.

Checklist

  • Criar módulo:
  • src/service/extractText.ts
  • Implementar detecção de tipo por:
  • extensão
  • MIME opcional
  • Criar resolver de conversor:
  resolveTextExtractor(inputType, preferredConverter?)
  • Ordem inicial para DOCX:
  • calibre
  • libreoffice
  • markitdown/pandoc se aplicável
  • Definir retorno:
  type ExtractTextResult = {
  text: string
  detectedInputType: string
  converterUsed: string
  durationMs: number
  warnings?: string[]
  }

Entregável

  • função extractTextFromFile(...)

────────────────────────────────────────────────────────────────────────────────

EPIC C — Modo service-only

Tarefa C1 — Criar flag de execução

Checklist

  • Adicionar env var:
  • SERVICE_MODE=true|false
  • No bootstrap (src/index.tsx), separar:
  • rotas web/UI
  • rotas de serviço/API
  • Quando SERVICE_MODE=true:
  • não montar páginas UI
  • não montar login/register/history/results
  • montar apenas health + API
  • Manter compatibilidade com modo original se necessário

Entregável

  • app sobe em modo só-serviço

────────────────────────────────────────────────────────────────────────────────

Tarefa C2 — Criar autenticação por token

Checklist

  • Adicionar env:
  • SERVICE_TOKEN
  • Criar middleware:
  • src/api/auth.ts
  • Ler header:
  • Authorization: Bearer
  • Permitir /healthz sem auth
  • Bloquear /api/* sem token válido
  • Padronizar erro 401:
  {
  "ok": false,
  "error": {
  "code": "UNAUTHORIZED",
  "message": "Invalid or missing service token"
  }
  }

Entregável

  • autenticação M2M funcionando

────────────────────────────────────────────────────────────────────────────────

EPIC D — API de extração

Tarefa D1 — Healthcheck

Checklist

  • Criar GET /healthz
  • Criar GET /api/healthz opcional
  • Retornar:
  • status do serviço
  • SERVICE_MODE
  • disponibilidade de conversores
  • Verificar presença real de:
  • libreoffice
  • calibre
  • pandoc
  • markitdown se existir

Entregável

  • health confiável para monitoramento e n8n

────────────────────────────────────────────────────────────────────────────────

Tarefa D2 — Endpoint POST /api/extract-text

Checklist

  • Criar rota:
  • POST /api/extract-text
  • Aceitar multipart form-data
  • Campo obrigatório:
  • file
  • Campos opcionais:
  • preferredConverter
  • filename
  • mimeType
  • formatHint
  • Validar tamanho do upload
  • Persistir em diretório temporário
  • Executar extração
  • Ler arquivo convertido TXT se o backend converter para TXT
  • Normalizar o texto
  • Devolver JSON

Entregável

  • endpoint consumível pelo n8n

────────────────────────────────────────────────────────────────────────────────

Tarefa D3 — Normalização de texto

Checklist

  • Criar módulo:
  • src/api/utils/text.ts
  • Aplicar:
  • trim
  • normalização de newline
  • remoção de NUL bytes
  • colapso de whitespace horizontal excessivo
  • preservação de parágrafos
  • limite opcional de tamanho
  • Adicionar warnings quando:
  • texto vier vazio
  • conversão tiver fallback
  • arquivo parecer corrompido

Entregável

  • texto mais estável para RAG

────────────────────────────────────────────────────────────────────────────────

EPIC E — Arquivos temporários e robustez

Tarefa E1 — Temp storage por request

Checklist

  • Criar requestId UUID por request
  • Estrutura:
  • /tmp/convertx-service//input
  • /tmp/convertx-service//output
  • Sanitizar nome do arquivo
  • Nunca confiar em path do usuário
  • Sempre limpar em finally
  • Logar falha de cleanup sem quebrar a resposta

Entregável

  • zero sujeira estrutural por request

────────────────────────────────────────────────────────────────────────────────

Tarefa E2 — Timeouts e limites

Checklist

  • Adicionar env:
  • MAX_UPLOAD_MB
  • REQUEST_TIMEOUT_MS
  • MAX_CONCURRENT_CONVERSIONS
  • Aplicar timeout por processo de conversão
  • Aplicar limite de upload
  • Rejeitar tipos não suportados
  • Padronizar erro:
  • PAYLOAD_TOO_LARGE
  • UNSUPPORTED_INPUT
  • CONVERSION_TIMEOUT

Entregável

  • serviço operacionalmente previsível

────────────────────────────────────────────────────────────────────────────────

EPIC F — Docker e operação

Tarefa F1 — Dockerfile / imagem do fork

Checklist

  • Reutilizar imagem base do projeto ou adaptar a atual
  • Garantir que conversores necessários estejam presentes
  • Garantir modo service-only no start
  • Expor porta única
  • Configurar env defaults seguros

Entregável

  • imagem convertx-service

────────────────────────────────────────────────────────────────────────────────

Tarefa F2 — Compose de operação

Checklist

  • Criar compose interno
  • Bind local de porta
  • ex: 127.0.0.1:3010:3000
  • Definir volume de dados se necessário
  • Definir restart policy
  • Definir token por env

Entregável

  • deploy repetível

────────────────────────────────────────────────────────────────────────────────

EPIC G — Integração com n8n

Tarefa G1 — Contrato n8n

Checklist

  • Documentar chamada HTTP Request no n8n
  • Exemplo com multipart binário
  • Exemplo de headers
  • Exemplo de resposta
  • Exemplo de mapping:
  • data = {{$json.text}}

Entregável

  • handoff claro para o time de automação

────────────────────────────────────────────────────────────────────────────────

EPIC H — Testes

Tarefa H1 — Testes funcionais

Checklist

  • DOCX simples
  • DOCX com acentos
  • DOCX com tabela
  • DOCX com listas
  • DOCX vazio
  • arquivo inválido/corrompido
  • extensão falsa
  • timeout forçado

Tarefa H2 — Testes operacionais

Checklist

  • concorrência 2-5 requests
  • cleanup após sucesso
  • cleanup após falha
  • auth inválida
  • healthcheck sem auth

Entregável

  • suíte mínima de confiança

────────────────────────────────────────────────────────────────────────────────

  1. ADR CURTA

ADR-001 — Fork do ConvertX como serviço interno de extração de texto

Status

Proposto

Contexto

O fluxo RAG atual depende de ingestão automática de arquivos do Nextcloud e precisa extrair texto de documentos, especialmente .docx, sem:

  • autenticação Google
  • UI humana
  • automação frágil de browser/login

Foi validado que o ConvertX, rodando em container, já converte DOCX -> TXT com sucesso usando sua stack interna (ex.: calibre/LibreOffice). Porém, a aplicação atual é orientada a UI e não expõe uma API simples/documentada para integração máquina-a-máquina.

Decisão

Fazer um fork do ConvertX e adicionar um modo service-only, com rotas internas dedicadas para automação, iniciando com:

  • GET /healthz
  • POST /api/extract-text

O serviço será protegido por token interno e consumido pelo n8n.

Racional

Essa abordagem:

  • reutiliza a stack de conversão já validada
  • evita desenvolver um conversor do zero
  • evita depender de Google
  • evita scraping da UI
  • reduz confusão operacional com clientes
  • mantém a extração como componente de infraestrutura isolado

Alternativas consideradas

1. Google como backend de conversão

Rejeitada por exigir autenticação e gerar complexidade externa indesejada.

2. LibreOffice no host, chamado diretamente

Viável, mas mais chato de operar, versionar e encapsular do que reaproveitar o container já funcional.

3. ConvertX UI com automação/scraping

Rejeitada por fragilidade e baixa mantenibilidade.

4. Serviço local custom do zero

Viável, mas desnecessário dado que o ConvertX já resolve a parte difícil da conversão.

Consequências positivas

  • integração limpa com n8n
  • backend interno sem UI
  • melhor separação de responsabilidades
  • facilidade de evolução para outros formatos

Consequências negativas

  • custo de manutenção do fork
  • necessidade de isolar mudanças para facilitar merge com upstream
  • necessidade de lidar com cleanup, auth e limites operacionais

Decisão operacional

Implementar primeiro um MVP de DOCX -> texto via POST /api/extract-text, depois expandir se necessário.

────────────────────────────────────────────────────────────────────────────────

  1. ESPECIFICAÇÃO DE ENDPOINTS

Base URL

Interna, por exemplo:

Autenticação

Header obrigatório para /api/*:

  Authorization: Bearer <SERVICE_TOKEN>

────────────────────────────────────────────────────────────────────────────────

3.1 GET /healthz

Objetivo

Sonda simples para monitoramento e readiness.

Auth

Não obrigatória

Request

  GET /healthz

Response 200

  {
  "ok": true,
  "service": "convertx-service",
  "serviceMode": true,
  "version": "0.1.0",
  "converters": {
  "libreoffice": true,
  "calibre": true,
  "pandoc": true,
  "markitdown": false
  }
  }

────────────────────────────────────────────────────────────────────────────────

3.2 GET /api/formats

Objetivo

Listar formatos e conversores disponíveis.

Auth

Obrigatória

Request

  GET /api/formats
  Authorization: Bearer <SERVICE_TOKEN>

Response 200

  {
  "ok": true,
  "extractText": {
  "inputs": ["docx", "txt", "md", "html", "odt"],
  "preferredConverters": ["calibre", "libreoffice", "pandoc", "markitdown"]
  }
  }

────────────────────────────────────────────────────────────────────────────────

3.3 POST /api/extract-text

Objetivo

Receber um arquivo e retornar texto extraído.

Auth

Obrigatória

Content-Type

multipart/form-data

Campos

Obrigatório

  • file — binário do arquivo

Opcionais

  • preferredConverter — string
  • exemplos: calibre, libreoffice
  • filename — string
  • mimeType — string
  • formatHint — string
  • ex.: docx

Exemplo cURL

  curl -X POST http://127.0.0.1:3010/api/extract-text \
  -H "Authorization: Bearer SEU_TOKEN" \
  -F "file=@contrato.docx" \
  -F "preferredConverter=calibre"

Response 200

  {
  "ok": true,
  "filename": "contrato.docx",
  "detectedInputType": "docx",
  "converter": "calibre",
  "text": "Conteúdo extraído do documento...",
  "meta": {
  "chars": 18432,
  "durationMs": 913,
  "warnings": []
  }
  }

Response 400 — arquivo ausente

  {
  "ok": false,
  "error": {
  "code": "MISSING_FILE",
  "message": "Field 'file' is required"
  }
  }

Response 400 — tipo não suportado

  {
  "ok": false,
  "error": {
  "code": "UNSUPPORTED_INPUT",
  "message": "Unsupported input type: xlsm"
  }
  }

Response 401 — auth inválida

  {
  "ok": false,
  "error": {
  "code": "UNAUTHORIZED",
  "message": "Invalid or missing service token"
  }
  }

Response 413 — arquivo grande

  {
  "ok": false,
  "error": {
  "code": "PAYLOAD_TOO_LARGE",
  "message": "File exceeds maximum allowed size"
  }
  }

Response 500 — falha de conversão

  {
  "ok": false,
  "error": {
  "code": "CONVERSION_FAILED",
  "message": "Failed to extract text from file"
  }
  }

────────────────────────────────────────────────────────────────────────────────

3.4 POST /api/convert

Objetivo

Conversão genérica de arquivo para formato alvo.

Status

Opcional / pós-MVP

Auth

Obrigatória

Request

Multipart:

  • file
  • target
  • preferredConverter opcional

Exemplo

  curl -X POST http://127.0.0.1:3010/api/convert \
  -H "Authorization: Bearer SEU_TOKEN" \
  -F "file=@arquivo.docx" \
  -F "target=txt"

Resposta possível

modo JSON

  {
  "ok": true,
  "filename": "arquivo.txt",
  "converter": "calibre",
  "contentBase64": "SGVsbG8uLi4="
  }

ou modo binário

retorno direto do arquivo convertido com:

  • Content-Type
  • Content-Disposition

Recomendação

Para o caso RAG, não priorizar este endpoint no MVP. Priorizar extract-text.

────────────────────────────────────────────────────────────────────────────────

3.5 Especificação de integração com n8n

Chamada

No HTTP Request node:

  Authorization: Bearer {{$env.CONVERTX_SERVICE_TOKEN}}
  • Send Binary Data: true
  • Binary Property: data
  • Content type: multipart/form-data

Resposta esperada

  {
  "ok": true,
  "text": "..."
  }

Mapping seguinte

No Set:

  {{ $json.text }}

para o campo:

  • data

────────────────────────────────────────────────────────────────────────────────

Recomendação final de execução

Ordem

  1. MVP extract-text
  2. health + auth + cleanup
  3. integração n8n
  4. testes DOCX reais
  5. só depois:
  • formats
  • convert
  • outros formatos