first commit

This commit is contained in:
gustavooth
2026-07-26 21:12:38 -03:00
commit e6badb0d79
133 changed files with 38368 additions and 0 deletions
+267
View File
@@ -0,0 +1,267 @@
# Leads Extractor
Extrator assíncrono de canais de podcast, entrevistados e contatos públicos. O
backend usa Rust, Actix, Tokio, Chromiumoxide, PostgreSQL e OpenAI; o frontend
usa SvelteKit, TypeScript e Tailwind CSS.
## Arquitetura
- A API Actix responde rápido e grava execuções na fila PostgreSQL.
- Workers Tokio reclamam jobs com `FOR UPDATE SKIP LOCKED` e executam os
pipelines com concorrência configurável.
- Cada navegação de documento e busca segue `perfil limpo -> aquecer a mesma
origem -> acessar` no Chromium headless. Assim, TLS, HTTP/2, Client Hints,
cookies e carregamento de assets são produzidos pelo navegador real; a sessão
e o processo são descartados ao fim. Downloads binários continuam em um
cliente HTTP isolado, sem reaproveitar conexões.
- A IA recebe DTOs preparados pelo backend e nunca possui acesso ao banco.
- Contatos são normalizados e deduplicados, preservando todas as origens e
evidências.
- O Docker Compose local executa somente o PostgreSQL; API e interface rodam
manualmente, facilitando logs, recompilação e depuração.
## Requisitos locais
- Docker com Compose
- Rust 1.96 ou compatível com edition 2024
- Node.js 24+ e npm
- Google Chrome/Chromium disponível no `PATH` para execução fora do container
## Desenvolvimento local
1. Inicie somente o PostgreSQL. O usuário, banco e senha `leads` são defaults
exclusivamente locais:
```bash
docker compose up -d postgres
```
Se a porta `5432` estiver ocupada:
```bash
POSTGRES_PORT=55432 docker compose up -d postgres
```
Nesse caso, ajuste também a porta em `backend/.env`.
2. Prepare o backend:
```bash
cp backend/.env.example backend/.env
```
Edite apenas a cópia local `backend/.env`. Além da OpenAI e da DataImpulse,
defina um usuário de acesso e uma senha exclusiva com pelo menos 12
caracteres:
- `AUTH_USERNAME`: obrigatório; não possui default seguro.
- `AUTH_PASSWORD`: obrigatória; mínimo de 12 caracteres.
- `AUTH_SESSION_TTL_SECS=43200`: duração padrão da sessão, em segundos.
- `AUTH_COOKIE_SECURE=false`: somente para HTTP local; use `true` sob HTTPS.
- `AUTH_COOKIE_SAME_SITE=strict`: default recomendado; `lax` também é
suportado, mas nenhum dos modos habilita autenticação cross-site.
Em seguida, inicie a API manualmente:
```bash
cd backend
cargo run
```
O backend cria `schema_migrations` e aplica as migrations pendentes no
startup. O PostgreSQL não executa SQL via `docker-entrypoint-initdb.d`.
`SERVER_PORT` é configurável e usa `8080` por padrão.
3. Em outro terminal, prepare o frontend:
```bash
cd frontend
npm ci
cp .env.example .env
npm run dev
```
Se a porta `8080` estiver ocupada, escolha outra em `SERVER_PORT` no
`backend/.env` e defina a mesma porta em `VITE_API_URL` no `frontend/.env`
antes de iniciar os dois processos.
4. Abra `http://localhost:5173` e autentique-se com as credenciais definidas no
`backend/.env`. Por padrão, a API local fica em `http://localhost:8080`.
## Operação local
O Compose não constrói nem inicia backend ou frontend. Ele gerencia somente o
PostgreSQL 18 pela imagem `postgres:18`:
```bash
docker compose config --quiet
docker compose up -d postgres
docker compose ps postgres
```
Execute `cargo run` em `backend/` e `npm run dev` em `frontend/`, em terminais
separados. Os endpoints locais são:
- Frontend: `http://localhost:5173`
- Backend: `http://localhost:8080`
- Healthcheck: `http://localhost:8080/api/health`
Para acompanhar ou encerrar apenas o banco:
```bash
docker compose logs -f postgres
docker compose down
```
`docker compose down` mantém o volume PostgreSQL. `docker compose down -v`
remove o banco e causa perda de dados; use apenas quando isso for intencional.
### Publicação
O Compose fornecido é destinado ao banco local. Em publicação, execute backend
e frontend com o gerenciador de processos ou orquestrador escolhido e mantenha
senhas, chave da OpenAI e credenciais da DataImpulse em um gerenciador de
segredos. Defina uma senha PostgreSQL forte, `FRONTEND_ORIGIN` com a origem
HTTPS exata e `AUTH_COOKIE_SECURE=true`. Não publique a API sem TLS.
A sessão de autenticação usa cookie `HttpOnly`, enviado pelo frontend com
credenciais incluídas, e política `SameSite` restritiva. O frontend usa
`POST /api/auth/login`, `GET /api/auth/session` e `POST /api/auth/logout`.
Frontend e API precisam permanecer **same-site**, inclusive quanto ao esquema
HTTP/HTTPS. CORS não torna cookies `SameSite=Strict` ou `Lax` utilizáveis entre
sites diferentes, portanto implantação cross-site não é suportada. Em
desenvolvimento, use o mesmo hostname nas duas URLs — por exemplo, não misture
`localhost` e `127.0.0.1`. Se forem origens diferentes dentro do mesmo site,
configure `FRONTEND_ORIGIN` com a origem exata do frontend.
O cliente adiciona automaticamente `X-CSRF-Protection: 1` a toda requisição
mutante, envia o cookie com `credentials: include` e o navegador fornece
`Origin`. Clientes manuais precisam enviar o mesmo cabeçalho e a origem
esperada. O login possui rate limit; excesso de tentativas recebe HTTP `429`.
Respostas privadas usam `Cache-Control: no-store` para evitar retenção por
caches do navegador ou de intermediários.
As sessões atuais vivem somente na memória do processo backend. Execute uma
única instância: reiniciar ou substituir o processo encerra todas as sessões e
exige novo login. Antes de usar múltiplas réplicas, mova o armazenamento de
sessões para um serviço compartilhado, como PostgreSQL ou Redis.
## Variáveis de ambiente
### Compose
| Variável | Default local | Quando alterar |
| --- | --- | --- |
| `POSTGRES_DB` | `leads_extractor` | Nome do banco |
| `POSTGRES_USER` | `leads` | Usuário do banco |
| `POSTGRES_PASSWORD` | `leads` | Obrigatória fora do ambiente local |
| `POSTGRES_PORT` | `5432` | Porta publicada no host |
Se alterar usuário, senha, banco ou porta do Compose, atualize também
`DATABASE_URL` em `backend/.env`. Senhas com caracteres reservados precisam
estar percent-encoded nessa URL.
### Backend
O arquivo de referência é `backend/.env.example`.
- `OPENAI_API_KEY`: obrigatória para resumos, categorização e extração por IA;
o servidor pode iniciar sem ela, mas esses fluxos falham de forma explícita.
- `OPENAI_MODEL`, `OPENAI_BASE_URL`, timeouts e retries: configuração do
provedor de IA.
- `AUTH_USERNAME`: usuário obrigatório para acessar a aplicação; não há valor
padrão seguro.
- `AUTH_PASSWORD`: senha obrigatória com pelo menos 12 caracteres.
- `AUTH_SESSION_TTL_SECS`: validade da sessão; default `43200` segundos.
- `AUTH_COOKIE_SECURE`: `false` apenas durante desenvolvimento HTTP local e
`true` quando a aplicação estiver sob HTTPS.
- `AUTH_COOKIE_SAME_SITE`: `strict` por padrão ou `lax`; ambos exigem frontend
e API same-site.
- `SERVER_HOST` e `SERVER_PORT`: endereço da API; a porta padrão é `8080`.
- `BROWSER_MIN_NAVIGATION_DELAY_MS` / `BROWSER_MAX_NAVIGATION_DELAY_MS`:
intervalo aleatório aplicado antes de cada sessão de navegador (padrão:
250015000 ms).
- `BROWSER_MACRO_PAUSE_*`: faixa de sessões e duração das pausas maiores;
padrões de 1520 sessões e 3090 segundos.
- `FRONTEND_ORIGIN`: origem exata autorizada pelo CORS e pela validação das
requisições mutantes.
- `DATAIMPULSE_PROXY_ENABLED`: fica ativo por padrão; desative somente para
diagnóstico local explícito.
- `DATAIMPULSE_PROXY_USERNAME` e `DATAIMPULSE_PROXY_PASSWORD`: obrigatórias
quando o proxy estiver ativo.
- `DATAIMPULSE_PROXY_HOST`, porta e país: parâmetros da DataImpulse. O login
efetivo nunca inclui `sessid`, portanto cada requisição usa o pool rotativo.
- `MAX_INTERVIEWEES_PER_RUN`: teto operacional por execução, aplicado também
sob concorrência. `0` processa todos; para o teste externo controlado use
`100` (valor já usado no `.env` local desta instalação).
- `WORKER_CONCURRENCY`, `BROWSER_CONCURRENCY`, limites de crawl e timeouts:
orçamento operacional.
- `MEDIA_DIR`: diretório local de logos, imagens e demais mídias coletadas.
- `BROWSER_NO_SANDBOX`: mantenha `false` localmente; habilite somente em um
ambiente isolado que não ofereça user namespaces.
Nunca comite `backend/.env`, `frontend/.env`, tokens, senhas, cookies ou
credenciais do proxy. Os `.dockerignore` também excluem esses arquivos do
contexto de build, mas isso não substitui um gerenciador de segredos.
### Frontend
O arquivo de referência é `frontend/.env.example`.
- `VITE_API_URL`: URL da API; localmente, `http://localhost:8080`. Deve usar o
mesmo `SERVER_PORT` configurado no backend.
- `VITE_API_POLL_INTERVAL`: intervalo de atualização do painel, em
milissegundos.
- `VITE_DEMO_MODE`: mantenha `false` para autenticação e dados reais. `true`
ativa somente a demonstração local explícita.
## Dados persistentes
- `postgres_data`: cluster PostgreSQL 18 gerenciado pelo Compose.
- `MEDIA_DIR`: diretório no host usado pelo backend iniciado manualmente para
logos, imagens e demais mídias coletadas.
Faça backup do volume e do diretório de mídia. Alterar ou adicionar migrations
não recria o banco: o backend registra cada versão aplicada em
`schema_migrations`.
## Fluxos
- **Busca:** procura canais de podcast e permite revisar a lista.
- **Entrevistados:** coleta vídeos, metadados e legendas; a IA identifica todos
os convidados e resolve duplicidades. A ordem é português/original
preferido e depois qualquer faixa original disponível, sem tradução
artificial. Transcrições longas são processadas em trechos sobrepostos.
- **Contatos:** pesquisa o nome/contexto no Google e percorre páginas em BFS até
cinco níveis, classificando cada contato como pessoal ou comercial. Links
adicionais escolhidos pela IA só são visitados quando possuem evidência na
página e entram novamente no mesmo ciclo agentivo até o quinto nível.
Falhas externas passam pelos retries com backoff, jitter, sessão/IP novo e
timeout de cada módulo. Se um item ainda falhar, o job é repetido e reaproveita
vídeos já concluídos como checkpoints. Falta de créditos pausa o mesmo job sem
perder a possibilidade de retomada pelo botão de retry. A resolução de
identidades possui um limite global de concorrência para reservar conexões do
pool PostgreSQL mesmo com várias execuções simultâneas.
## Verificações
```bash
docker compose config --quiet
docker compose up -d postgres
cd backend
cargo fmt --all -- --check
cargo check --all-targets
cargo test --all-targets
cargo clippy --all-targets -- -D warnings
cd ../frontend
npm ci
npm run check
npm run build
```
O crawler aceita apenas URLs HTTP/HTTPS públicas e bloqueia destinos locais ou
privados. Não há automação de login, CAPTCHA ou áreas protegidas.