diff --git a/PLANO_FORK_SERVICO.md b/PLANO_FORK_SERVICO.md new file mode 100644 index 0000000..edcaa03 --- /dev/null +++ b/PLANO_FORK_SERVICO.md @@ -0,0 +1,142 @@ +# Verificação do INSTRUCOES_FORK.md e Plano de Execução + +## 1. Verificação do documento + +### Consistência com o código atual + +O arquivo **INSTRUCOES_FORK.md** foi confrontado com o repositório ConvertX. Resultado: + +| Aspecto | Documento | Código | Status | +|--------|-----------|--------|--------| +| Stack | Elysia, Bun | `src/index.tsx` usa Elysia, Bun | ✅ | +| handleConvert | Acoplado a UI/job/db | `src/converters/main.ts` usa `db`, `jobId`, dirs por user | ✅ | +| Assinatura handleConvert | fileNames, dirs, convertTo, converter, jobId | `handleConvert(fileNames, userUploadsDir, userOutputDir, convertTo, converterName, jobId)` | ✅ | +| Núcleo de conversão | mainConverter / conversores por engine | `mainConverter()` interno + `properties[].converter` | ✅ | +| Upload | multipart em dir user/job | `upload.tsx`: `Bun.write(userUploadsDir + sanitize(name), file)` | ✅ | +| Convert | chama handleConvert com body | `convert.tsx`: body.convert_to, file_names, jobId, user | ✅ | +| Health | existe algo básico | `healthcheck.tsx`: GET /healthcheck → `{ status: "ok" }` | ✅ (expandir) | +| Conversores DOCX→TXT | calibre, libreoffice, markitdown | `calibre`, `libreoffice`, `markitdown` em `main.ts` | ✅ | + +**Conclusão:** O documento está alinhado com o código. A estratégia de extrair uma função pura de conversão e adicionar uma camada `src/api/` sem alterar agressivamente o fluxo web é viável e coerente com a estrutura atual. + +### Pontos de atenção + +1. **mainConverter não é exportado** — a extração pode reutilizar a mesma lógica (normalizeFiletype, properties, escolha de conversor) ou exportar/refatorar `mainConverter` com cuidado para não quebrar a UI. +2. **Cookie jobId** — em `handleConvert`, `jobId` só é usado para `query.run(jobId.value, ...)`. Uma função pura não deve receber jobId nem escrever em `file_names`. +3. **Rotas Elysia** — O app monta plugins por `.use(user).use(root).use(upload)...`. O modo service-only pode ser um branch no bootstrap: `if (SERVICE_MODE) app.use(apiRoutes); else { ... use(convert) ... }`. +4. **JWT / user** — Todas as rotas atuais passam por `userService` e auth. As rotas em `/api/*` devem usar apenas o middleware de token (Bearer), sem JWT de usuário. + +--- + +## 2. Plano de execução + +Resumo do plano em **fases e épicos**, seguindo a ordem recomendada no próprio INSTRUCOES_FORK.md (e o checklist técnico). + +--- + +### Fase 0 — Preparação (antes de codar) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 0.1 | Criar fork (se ainda for o repo original) e branch `feat/service-mode-api` | Branch de trabalho | +| 0.2 | Documentar hash/tag do upstream usado | README ou doc interno | +| 0.3 | Mapear fluxo atual: upload → convert → output (onde está cada passo no código) | Doc “conversion flow map” | + +**Arquivos a inspecionar:** `src/index.tsx`, `src/pages/upload.tsx`, `src/pages/convert.tsx`, `src/converters/main.ts` (já feito na verificação acima). + +--- + +### Fase 1 — Desacoplamento (Epic B) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 1.1 | Extrair função backend pura de conversão (ex.: `convertSingleFile`) em `src/service/convertSingleFile.ts` | Função sem cookie/user/job/db | +| 1.2 | Criar `src/service/extractText.ts` com `resolveTextExtractor(inputType, preferredConverter?)` e `extractTextFromFile(...)` | Camada “extração de texto” reutilizável | + +**Critério:** Uma chamada tipo `convertSingleFile({ inputPath, outputDir, target: 'txt', preferredConverter: 'calibre' })` deve converter e retornar `outputPath`, `converterUsed`, `target`, `durationMs`, sem tocar em SQLite nem em cookies. + +--- + +### Fase 2 — Modo service-only (Epic C) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 2.1 | Introduzir `SERVICE_MODE=true|false` e no bootstrap (ex.: `src/index.tsx`) montar só health + API quando true | App sobe sem UI | +| 2.2 | Criar `src/api/auth.ts`: middleware que exige `Authorization: Bearer ` em `/api/*`; exceção para `/healthz` | Auth M2M | + +**Variáveis:** `SERVICE_MODE`, `SERVICE_TOKEN`. + +--- + +### Fase 3 — API de extração (Epic D) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 3.1 | GET `/healthz`: ok, service, serviceMode, converters (libreoffice, calibre, pandoc, markitdown) | Health confiável | +| 3.2 | POST `/api/extract-text`: multipart `file`, opcionais (filename, mimeType, preferredConverter, formatHint); temp dir → extração → normalização → JSON | MVP consumível pelo n8n | +| 3.3 | Módulo `src/api/utils/text.ts`: trim, newline, NUL, whitespace, limites; warnings quando texto vazio ou fallback | Texto estável para RAG | + +**Contrato:** Conforme especificação de endpoints no INSTRUCOES_FORK.md (respostas 200/400/401/413/500 com `ok`, `error.code`, `error.message`). + +--- + +### Fase 4 — Robustez (Epic E) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 4.1 | Temp por request: `/tmp/convertx-service//input|output`, sanitização de nome, cleanup em `finally`, log de falha de cleanup | Zero sujeira por request | +| 4.2 | Env: `MAX_UPLOAD_MB`, `REQUEST_TIMEOUT_MS`, `MAX_CONCURRENT_CONVERSIONS`; rejeitar tipos não suportados e payload grande; erros padronizados (PAYLOAD_TOO_LARGE, UNSUPPORTED_INPUT, CONVERSION_TIMEOUT) | Comportamento previsível | + +--- + +### Fase 5 — Docker e operação (Epic F) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 5.1 | Dockerfile (ou adaptar o existente): modo service-only no start, conversores presentes, porta única | Imagem `convertx-service` | +| 5.2 | Compose exemplo: bind 127.0.0.1:3010:3000, env SERVICE_MODE, SERVICE_TOKEN, MAX_UPLOAD_MB, REQUEST_TIMEOUT_MS | Deploy repetível | + +--- + +### Fase 6 — Integração e testes (Epics G e H) + +| # | Tarefa | Entregável | +|---|--------|------------| +| 6.1 | Documentar no n8n: HTTP Request POST `/api/extract-text`, Bearer token, multipart, mapping `$json.text` | Handoff para automação | +| 6.2 | Testes: DOCX simples/acentos/tabela; arquivo inválido; extensão falsa; timeout; concorrência 2–5; cleanup após sucesso/falha; health sem auth; 401 sem token | Suíte mínima de confiança | + +--- + +## 3. Ordem recomendada (build order) + +1. **Extrair/consolidar** função backend de conversão (Fase 1). +2. **Criar** `/api/extract-text` (Fase 3.2), usando essa função e temp dir simples. +3. **Adicionar** auth + health (Fases 2 e 3.1) e cleanup/limites (Fase 4). +4. **Dockerizar** modo service-only (Fase 5). +5. **Integrar** no n8n e validar com DOCX reais (Fase 6). +6. **Depois** considerar `/api/convert` genérico e `/api/formats` (conforme Sprint 3 do documento). + +--- + +## 4. Definição de pronto (MVP) + +O fork estará pronto para uso no n8n quando: + +- [ ] POST `/api/extract-text` aceitar DOCX e devolver texto confiável. +- [ ] Token interno obrigatório em `/api/*`. +- [ ] Limpeza de temporários garantida. +- [ ] GET `/healthz` exposto. +- [ ] Container sobe sem UI (`SERVICE_MODE=true`). +- [ ] Um único HTTP Request no n8n consome o serviço e usa `$json.text`. + +--- + +## 5. Artefatos já presentes no INSTRUCOES_FORK.md + +O próprio documento já inclui os três artefatos pedidos: + +1. **Checklist técnico por tarefa** — EPICs A a H com checklists e entregáveis. +2. **ADR curta** — ADR-001 (Fork do ConvertX como serviço interno de extração de texto). +3. **Especificação de endpoints** — GET `/healthz`, GET `/api/formats`, POST `/api/extract-text`, POST `/api/convert` (pós-MVP), com payloads e exemplos cURL/n8n. + +Este arquivo (**PLANO_FORK_SERVICO.md**) serve como **verificação + plano de execução** em uma única visão, para seguir fase a fase sem perder o fio do INSTRUCOES_FORK.md.