Files
leadcast/README.md
T
2026-07-26 21:17:57 -03:00

268 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.