# 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: 2500–15000 ms). - `BROWSER_MACRO_PAUSE_*`: faixa de sessões e duração das pausas maiores; padrões de 15–20 sessões e 30–90 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.