Files
SnapOtter/apps/docs/pt-BR/guide/architecture.md
T

125 lines
10 KiB
Markdown
Raw Normal View History

---
description: "Estrutura do monorepo, arquitetura de apps e pacotes, ciclo de vida das requisições e uso de recursos do SnapOtter."
i18n_source_hash: 50e076925c4b
i18n_provenance: human
i18n_output_hash: e541eef7cbf1
i18n_hash_version: 2
---
# Arquitetura {#architecture}
O SnapOtter é um monorepo gerenciado com pnpm workspaces e Turborepo. Ele é implantado como uma pilha Docker Compose de 3 contêineres: a imagem do app SnapOtter, o PostgreSQL 17 e o Redis 8.
## Estrutura do projeto {#project-structure}
```
snapotter/
├── apps/
│ ├── api/ # Fastify backend
│ ├── web/ # React + Vite frontend
│ └── docs/ # This VitePress site
├── packages/
│ ├── image-engine/ # Sharp-based image operations
│ ├── media-engine/ # FFmpeg spawn + progress parsing
│ ├── doc-engine/ # qpdf, LibreOffice, ghostscript wrappers
│ ├── ai/ # Python AI model bridge
│ └── shared/ # Types, constants, i18n
└── docker/ # Dockerfile and Compose config
```
## Pacotes {#packages}
### `@snapotter/image-engine` {#snapotter-image-engine}
A biblioteca principal de processamento de imagem construída sobre o [Sharp](https://sharp.pixelplumbing.com/). Ela lida com todas as operações que não são de IA: redimensionar, recortar, girar, espelhar, converter, comprimir, remover metadados e ajustes de cor (brilho, contraste, saturação, escala de cinza, sépia, inversão, canais de cor).
Este pacote não tem dependências de rede e roda inteiramente em processo.
### `@snapotter/ai` {#snapotter-ai}
Uma camada de ponte que chama tempos de execução nativos e Python ML. A maioria das ferramentas Python usa um dispatcher persistente que pré-importa bibliotecas pesadas (PIL, NumPy, MediaPipe, rembg) para que as chamadas subsequentes ignorem a sobrecarga de importação. OCR é isolado desse ambiente compartilhado mutável: `fast` invoca Tesseract nativo, enquanto `balanced` e `best` usam um JSONL dispatcher persistente dedicado fixado na geração RapidOCR/ONNX ativa e imutável. Cada solicitação contém um generation lease. A ativação primeiro executa um smoke test em um candidato e depois alterna atomicamente para seu dispatcher. O dispatcher anterior é drenado antes que sua geração seja coletada como lixo.
**Os modelos não são pré-carregados.** Cada script de ferramenta carrega os pesos do seu modelo do disco no momento da requisição e os descarta quando a requisição termina. Consulte [Uso de recursos](#resource-footprint) para o perfil de memória completo.
Operações suportadas: remoção de fundo (rembg/BiRefNet), upscaling (RealESRGAN), desfoque de rosto (MediaPipe), aprimoramento de rosto (GFPGAN/CodeFormer), apagamento de objeto (LaMa ONNX), OCR (Tesseract e RapidOCR com modelos PP-OCR ONNX), colorização (DDColor), remoção de ruído, remoção de olhos vermelhos, restauração de fotos, foto de passaporte geração, correção de transparência (BiRefNet HR-matting) e redimensionamento com reconhecimento de conteúdo (Go caire binário).
Os scripts Python residem em `packages/ai/python/`. Grandes pacotes de modelos opcionais são instalados sob demanda no volume `/data/ai` persistente. O OCR preciso usa artefatos assinados e específicos da plataforma; a camada Tesseract integrada não requer download de pacote de modelo.
### `@snapotter/shared` {#snapotter-shared}
Tipos TypeScript compartilhados, constantes (como `APP_VERSION` e as definições de ferramentas) e strings de tradução i18n usadas tanto pelo frontend quanto pelo backend.
## Aplicações {#applications}
### API (`apps/api`) {#api-apps-api}
Um servidor Fastify v5 que expõe 243 rotas de ferramentas em cinco modalidades (image, video, audio, PDF, file) e lida com:
- Uploads de arquivos, gerenciamento de espaço de trabalho temporário e armazenamento persistente de arquivos
- Biblioteca de arquivos do usuário (tabela `user_files`): uma edição salva é armazenada por padrão como um novo arquivo independente, ou como uma versão vinculada ao pai quando você sobrescreve o original. Ela registra quais ferramentas foram aplicadas (`toolChain`) e recebe uma miniatura gerada automaticamente para a página Files
- Execução de ferramentas (roteia cada requisição de ferramenta para o motor de imagem ou para a ponte de IA)
- Orquestração de pipelines (encadeamento de várias ferramentas em sequência)
- Processamento em lote com controle de concorrência via filas de jobs do BullMQ (pools: image, media, ai, docs, system)
- Autenticação de usuários, RBAC (funções admin/user com um conjunto completo de permissões), gerenciamento de chaves de API e limitação de taxa
- Gerenciamento de equipes - CRUD apenas para admin; os usuários são atribuídos a uma equipe por meio do campo `team` no seu perfil
- Configurações de runtime - um armazenamento chave-valor na tabela `settings` que controla `disabledTools`, `enableExperimentalTools`, `loginAttemptLimit` e outros parâmetros operacionais sem reimplantar
- Marca personalizada e preferências de runtime por meio de configurações respaldadas pelo banco de dados
- Documentação Scalar/OpenAPI em `/api/docs`
- Serviço do frontend compilado como uma SPA em produção
Dependências principais: Fastify, Drizzle ORM (pg-core, node-postgres), Sharp, BullMQ, ioredis, Zod para validação.
O servidor lida com o encerramento gracioso em SIGTERM/SIGINT: ele drena as conexões HTTP, para os workers do BullMQ, desliga o dispatcher Python e fecha a conexão com o banco de dados.
### Web (`apps/web`) {#web-apps-web}
Um app de página única em React 19 construído com Vite. Usa Zustand para gerenciamento de estado, Tailwind CSS v4 para estilização e Lucide para ícones. Comunica-se com a API por REST e SSE (para acompanhamento de progresso).
As páginas incluem um espaço de trabalho de ferramentas, uma página Files para gerenciar uploads e resultados persistentes, um construtor de automação/pipelines e um painel de configurações de admin.
O frontend compilado é servido pelo backend Fastify em produção, portanto não há um servidor web separado no contêiner Docker.
### Docs (`apps/docs`) {#docs-apps-docs}
Este site VitePress. Implantado no Cloudflare Pages automaticamente a cada push para `main`.
## Como uma requisição flui {#how-a-request-flows}
1. O usuário escolhe uma ferramenta na interface web e envia um arquivo.
2. O frontend envia um POST multipart para `/api/v1/tools/:section/:toolId` com o arquivo e as configurações.
3. A rota da API valida a entrada com o Zod e depois despacha o processamento.
4. Para ferramentas padrão, o job é enfileirado no pool BullMQ apropriado (image, media ou docs, conforme a modalidade). O worker BullMQ em processo orienta automaticamente a imagem com base nos metadados EXIF, executa a função de processamento da ferramenta e retorna o resultado.
5. Para a maioria das ferramentas de IA, a ponte TypeScript envia uma solicitação ao Python dispatcher persistente. Em vez disso, o OCR rápido invoca Tesseract, e o OCR preciso inicia o executável fixado a partir da geração OCR imutável ativa. A camada OCR solicitada é fixada na entrada e nunca é alterada silenciosamente durante a execução.
6. O progresso do job é persistido na tabela `jobs` no PostgreSQL, de modo que o estado sobrevive a reinícios do contêiner. As atualizações em tempo real são entregues via SSE em `/api/v1/jobs/:jobId/progress`.
7. A API retorna um `jobId` e um `downloadUrl`. O usuário baixa o arquivo processado de `/api/v1/download/:jobId/:filename`.
Para pipelines, a API alimenta a saída de cada etapa como entrada da próxima, executando-as em sequência.
Para o processamento em lote, a API usa flows do BullMQ com jobs filhos por etapa e retorna um arquivo ZIP com todos os arquivos processados.
## Uso de recursos {#resource-footprint}
O SnapOtter foi projetado para baixo uso de memória em repouso. Nada é pré-carregado ou mantido aquecido na inicialização.
### Em repouso {#at-idle}
O processo Node.js/Fastify, o PostgreSQL e o Redis estão em execução. A RAM típica em repouso é de **cerca de 200 a 300 MB** entre os três contêineres (processo Node.js, Postgres e Redis). Sem processo Python, sem pesos de modelo na memória.
### O que inicia, e quando {#what-starts-and-when}
| Componente | Inicia quando | Memória enquanto ativo |
|-----------|-------------|---------------------|
| Servidor Fastify + Postgres + Redis | Início do contêiner | ~200-300 MB no total |
| Workers do BullMQ | Início do contêiner (em processo) | Um worker por pool (image, media, ai, docs, system) |
| Dispatcher Python | Primeira requisição de ferramenta de IA | Interpretador Python + bibliotecas pré-importadas (PIL, NumPy, MediaPipe, rembg) - sem pesos de modelo |
| Pesos de modelo de IA | Durante a requisição da ferramenta específica | Carregados do disco, liberados quando a requisição termina |
### Carregamento de modelos {#model-loading}
Todos os arquivos de pesos de modelo (somando vários GB) ficam no disco em `/opt/models/` o tempo todo. Cada script de ferramenta de IA carrega na memória apenas o(s) seu(s) próprio(s) modelo(s) durante uma requisição e depois os libera. Alguns scripts chamam explicitamente `del model` e `torch.cuda.empty_cache()` após a inferência para garantir que a memória seja devolvida imediatamente.
Não há cache de modelo entre requisições. Executar a mesma ferramenta de IA em sequência recarrega o modelo a cada vez. Isso mantém a memória em repouso próxima de zero, ao custo de um atraso de carregamento do modelo em cada requisição de IA.
### Cold start da primeira requisição de IA {#first-ai-request-cold-start}
O dispatcher Python não está em execução quando o contêiner inicia. A primeira requisição de IA dispara duas coisas em paralelo: o dispatcher começa a aquecer em segundo plano e a própria requisição recorre a gerar um subprocesso Python único e avulso. Assim que o dispatcher sinaliza que está pronto, todas as requisições de IA subsequentes o usam diretamente e evitam o custo de gerar subprocessos.