diff --git a/PROXY_INTEGRATION.md b/PROXY_INTEGRATION.md deleted file mode 100644 index be04b10..0000000 --- a/PROXY_INTEGRATION.md +++ /dev/null @@ -1,268 +0,0 @@ -# Integração do Sistema de Proxies - -Este documento descreve como o sistema de proxies foi integrado à API do YouTube. - -## Visão Geral - -O sistema de proxies foi implementado para melhorar a confiabilidade da API, usando proxies armazenados em um banco de dados PostgreSQL. Quando um proxy falha, ele é automaticamente removido e outro proxy é testado. - -## Arquitetura - -### Arquivos Criados - -1. **database.py**: Módulo de conexão e operações com PostgreSQL - - `get_db_connection()`: Cria conexão com o banco - - `get_latest_proxy()`: Busca o melhor proxy disponível (baseado em métricas) - - `delete_proxy(proxy_id)`: Marca um proxy como inativo e incrementa failure_count - - `mark_proxy_success(proxy_id)`: Marca sucesso e incrementa success_count - - `format_proxy_url(proxy)`: Formata proxy no padrão yt_dlp (com suporte a autenticação) - -2. **proxy_manager.py**: Lógica de retry automático com proxies - - `is_proxy_error(error_msg)`: Identifica se um erro é relacionado a proxy - - `execute_with_proxy_retry()`: Executa operações com retry automático - -### Estrutura da Tabela de Proxies - -```sql -CREATE TABLE proxies ( - id SERIAL PRIMARY KEY, - ip_address VARCHAR(255) NOT NULL, - port INTEGER NOT NULL, - protocol VARCHAR(10) NOT NULL DEFAULT 'http', - username VARCHAR(255), -- Autenticação (opcional) - password VARCHAR(255), -- Autenticação (opcional) - country_code VARCHAR(10), -- Código do país (ex: US, BR) - country_name VARCHAR(100), -- Nome do país - city VARCHAR(100), -- Cidade - is_active BOOLEAN DEFAULT TRUE, -- Proxy está ativo? - is_anonymous BOOLEAN DEFAULT FALSE, -- Proxy é anônimo? - response_time_ms INTEGER, -- Tempo de resposta em ms - last_checked_at TIMESTAMP, -- Última verificação - last_successful_at TIMESTAMP, -- Último sucesso - failure_count INTEGER DEFAULT 0, -- Contador de falhas - success_count INTEGER DEFAULT 0, -- Contador de sucessos - usage VARCHAR(50), -- Uso do proxy - source VARCHAR(100), -- Fonte do proxy - notes TEXT, -- Notas adicionais - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - -## Configuração - -### 1. Variáveis de Ambiente - -Crie um arquivo `.env` baseado no `.env.example`: - -```bash -DB_HOST=seu_host_postgresql -DB_PORT=5432 -DB_NAME=seu_banco -DB_USER=seu_usuario -DB_PASSWORD=sua_senha -``` - -### 2. Docker Compose - -O `docker-compose.yml` já está configurado para carregar as variáveis de ambiente. Use: - -```bash -docker-compose up -d --build -``` - -## Como Funciona - -### Fluxo de Execução - -1. **Seleção Inteligente de Proxy**: A cada requisição, o sistema busca o melhor proxy disponível baseado em: - - Proxies ativos (`is_active = TRUE`) - - Último sucesso mais recente (`last_successful_at DESC`) - - Menor tempo de resposta (`response_time_ms ASC`) - - Maior taxa de sucesso (`success_count / (success_count + failure_count)`) - - Mais recentemente adicionado (`created_at DESC`) - -2. **Tentativa de Execução**: Tenta executar a operação usando o proxy selecionado - - Suporta autenticação automática se o proxy tiver `username` e `password` - - Formato: `protocol://username:password@ip_address:port` - - **Timeout configurado**: 8 segundos para conexão com proxy - -3. **Detecção de Erro**: Se houver erro relacionado a proxy (timeout, connection refused, etc.) - -4. **Desativação do Proxy**: O proxy com problema é marcado como inativo - - `is_active = FALSE` - - `failure_count` incrementado - - `last_checked_at` e `updated_at` atualizados - - **Nota**: O proxy NÃO é deletado, apenas desativado - -5. **Atualização de Sucesso**: Quando a operação é bem-sucedida - - `success_count` incrementado - - `last_successful_at` atualizado - - `is_active = TRUE` (reativa o proxy se estava inativo) - - `last_checked_at` e `updated_at` atualizados - -6. **Retry**: Busca outro proxy ativo e tenta novamente - -7. **Limite de Tentativas**: Máximo de 10 tentativas (configurável) - -### Performance e Timeout - -- **Socket Timeout**: 8 segundos por tentativa de proxy -- **Retry do yt_dlp**: Desabilitado (`retries: 0`) -- **Vantagem**: Ao detectar erro, troca IMEDIATAMENTE de proxy sem tentar novamente no mesmo -- **Comportamento**: - - ❌ **ANTES**: Proxy ruim → tenta 3x no mesmo → 24 segundos perdidos → troca - - ✅ **AGORA**: Proxy ruim → erro após 8s → remove → busca outro → 8 segundos -- **Tempo máximo de espera**: ~80 segundos (10 proxies × 8 segundos cada) -- **Nota**: Se precisar de timeout diferente, altere `socket_timeout` nas opções do yt_dlp - -### Palavras-chave de Erro de Proxy - -O sistema identifica erros de proxy por estas palavras-chave: -- proxy -- connection -- timeout -- timed out -- refused -- unreachable -- unable to connect -- network -- failed to connect -- connection reset -- read timed out -- http error 407 (proxy authentication) -- tunnel connection failed - -### Erros NÃO Tratados como Proxy - -Estes erros NÃO resultam em troca de proxy (são erros legítimos do vídeo): -- "requested format is not available" - Formato solicitado não existe -- "video unavailable" - Vídeo indisponível/removido -- "private video" - Vídeo privado -- "age restricted" - Vídeo com restrição de idade - -Quando esses erros ocorrem, o sistema **não** descarta o proxy e retorna o erro imediatamente. - -### Endpoints Integrados - -Todos os endpoints que usam yt_dlp foram integrados: - -1. **GET /get-video-metadata**: Obtém metadados de vídeos -2. **GET /download-video**: Download de vídeos -3. **GET /search**: Busca de vídeos -4. **GET /list-formats**: Lista formatos disponíveis - -### Tratamento de Erros - -- **ProxyError (503)**: Todos os proxies falharam ou não há proxies disponíveis -- **Exception (500)**: Erros não relacionados a proxy - -## Alimentando o Banco de Proxies - -Para adicionar proxies ao banco, você pode usar o serviço de scraper de proxies que já foi criado. - -### Exemplos de Inserção Manual - -**Proxies sem autenticação:** -```sql -INSERT INTO proxies (ip_address, port, protocol, is_active, is_anonymous, country_code) -VALUES - ('123.456.789.10', 8080, 'http', TRUE, FALSE, 'US'), - ('98.765.432.10', 3128, 'https', TRUE, TRUE, 'BR'), - ('45.67.89.100', 1080, 'socks5', TRUE, TRUE, 'DE'); -``` - -**Proxies com autenticação:** -```sql -INSERT INTO proxies (ip_address, port, protocol, username, password, is_active) -VALUES - ('premium-proxy.example.com', 8080, 'http', 'user123', 'pass456', TRUE), - ('secure-proxy.example.com', 3128, 'https', 'myuser', 'mypass', TRUE); -``` - -### Integração com Scraper - -Quando o serviço de scraper adicionar novos proxies, deve incluir: -- `ip_address`, `port`, `protocol` (obrigatórios) -- `country_code`, `country_name`, `city` (se disponível) -- `is_anonymous` (se detectado) -- `response_time_ms` (tempo de resposta inicial) -- `source` (fonte do scraper) -- `is_active = TRUE` por padrão - -## Monitoramento - -O sistema imprime logs úteis no console: - -``` -Tentativa 1: Usando proxy http://41.65.160.173:8080 (ID: 42) -Erro na tentativa 1: Connection to 41.65.160.173 timed out -Erro identificado como erro de proxy. Removendo proxy ID 42 -Proxy 42 desativado: True - -Tentativa 2: Usando proxy http://98.765.432.10:3128 (ID: 43) -Operação concluída com sucesso na tentativa 2 -Proxy (id 43) marcado como sucesso -``` - -**Importante**: Não haverá mais mensagens como "Retrying (1/3)..." porque desabilitamos o retry interno do yt_dlp. Cada erro resulta em troca imediata de proxy. - -## Vantagens - -1. **Alta Disponibilidade**: Se um proxy falhar, outro é usado automaticamente -2. **Seleção Inteligente**: Proxies são escolhidos baseado em performance e histórico -3. **Auto-recuperação**: Proxies são desativados quando falham, mas podem ser reativados em sucesso futuro -4. **Métricas Automáticas**: Sistema rastreia sucesso/falha e tempo de resposta automaticamente -5. **Suporte a Autenticação**: Proxies com username/password são suportados automaticamente -6. **Sem Intervenção Manual**: O sistema gerencia proxies de forma autônoma -7. **Preservação de Dados**: Proxies não são deletados, apenas desativados -8. **Fácil Integração**: Novo serviço de scraper pode adicionar proxies facilmente - -## Monitoramento e Análise - -### Queries Úteis - -**Ver estatísticas de proxies:** -```sql -SELECT - COUNT(*) as total, - COUNT(*) FILTER (WHERE is_active = TRUE) as ativos, - COUNT(*) FILTER (WHERE is_active = FALSE) as inativos, - AVG(response_time_ms) as tempo_resposta_medio, - SUM(success_count) as total_sucessos, - SUM(failure_count) as total_falhas -FROM proxies; -``` - -**Ver top 10 melhores proxies:** -```sql -SELECT - ip_address, port, protocol, - success_count, failure_count, - ROUND((success_count::FLOAT / NULLIF(success_count + failure_count, 0) * 100), 2) as taxa_sucesso, - response_time_ms, - last_successful_at -FROM proxies -WHERE is_active = TRUE -ORDER BY - last_successful_at DESC NULLS LAST, - response_time_ms ASC NULLS LAST -LIMIT 10; -``` - -**Reativar proxies desativados há mais de 24h (útil para retry):** -```sql -UPDATE proxies -SET is_active = TRUE, updated_at = NOW() -WHERE is_active = FALSE - AND updated_at < NOW() - INTERVAL '24 hours'; -``` - -## Próximos Passos Sugeridos - -1. ✅ Integrar com o serviço de scraper de proxies -2. ✅ Implementar métricas de sucesso/falha por proxy (CONCLUÍDO) -3. ✅ Implementar sistema de priorização de proxies (CONCLUÍDO) -4. Criar endpoint de administração para visualizar estatísticas de proxies -5. Implementar job periódico para reativar proxies após período de cooldown -6. Adicionar alertas quando número de proxies ativos cair abaixo de threshold diff --git a/main.py b/main.py index 256ba55..9a1cdca 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,6 @@ from youtube_transcript_api.formatters import SRTFormatter from youtube_transcript_api._errors import TranscriptsDisabled, NoTranscriptFound from yt_dlp import YoutubeDL from utils import extract_video_id, sanitize_title -from proxy_manager import execute_with_proxy_retry, ProxyError app = FastAPI( title="YouTube Transcript, Download and Metadata API", @@ -77,7 +76,7 @@ def get_video_metadata( } try: - def extract_metadata(ydl): + with YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(target, download=False, process=False) if not info or 'title' not in info: @@ -102,17 +101,6 @@ def get_video_metadata( if 'title' not in info: info['title'] = f"Vídeo {videoId or 'desconhecido'}" - - return info - - info = execute_with_proxy_retry(ydl_opts, extract_metadata, retry_per_proxy=1, max_proxies_to_try=3) - - except ProxyError as e: - error_msg = str(e).replace('\n', ' ').strip() - raise HTTPException( - status_code=503, - detail=f"Erro com proxies: {error_msg}" - ) except Exception as e: error_msg = str(e).replace('\n', ' ').strip() try: @@ -163,7 +151,7 @@ def download_video( quality_map = { "low": "bestvideo[height<=480]+bestaudio/best[height<=480]/bestvideo[height<=480]/best[height<=480]/best", "medium": "bestvideo[height<=720]+bestaudio/best[height<=720]/bestvideo[height<=720]/best[height<=720]/best", - "high": "bestvideo+bestaudio/best" + "high": "bestvideo[height>=1080]+bestaudio/bestvideo+bestaudio/best" } qualidade = qualidade.lower() if qualidade not in quality_map: @@ -175,24 +163,12 @@ def download_video( unique_id = str(uuid.uuid4()) output_template = os.path.join(videos_dir, f"{unique_id}.%(ext)s") - # Opções base para extração de metadados (operação rápida com proxy) metadata_opts = { "quiet": True, - "no_warnings": True, "skip_download": True, "nocheckcertificate": True, - "socket_timeout": 8, - "retries": 0, - "force_ipv4": True, - "geo_bypass": True, - "extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}}, - "http_headers": { - "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", - "User-Agent": "com.google.android.youtube/19.17.36 (Linux; U; Android 13) gzip", - }, } - # Opções para download (operação pesada - tentará com e sem proxy) download_opts = { "format": quality_map[qualidade], "outtmpl": output_template, @@ -200,36 +176,13 @@ def download_video( "noplaylist": True, "merge_output_format": "mp4", "nocheckcertificate": True, - "socket_timeout": 45, # Timeout menor para detectar falha de proxy rapidamente - "retries": 0, - "extractor_retries": 0, - "force_ipv4": True, - "geo_bypass": True, - "fragment_retries": 3, - "file_access_retries": 3, - "extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}}, - "http_headers": { - "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", - "User-Agent": "com.google.android.youtube/19.17.36 (Linux; U; Android 13) gzip", - }, - "http_chunk_size": 1048576, # 1MB chunks } try: - # ETAPA 1: Extrair metadados COM PROXY (operação rápida) - def extract_metadata_operation(ydl): - info = ydl.extract_info(target, download=False) - if not info or 'title' not in info: + with YoutubeDL(metadata_opts) as ydl: + metadata = ydl.extract_info(target, download=False) + if not metadata or 'title' not in metadata: raise Exception("Não foi possível extrair metadados do vídeo") - return info - - # Tenta apenas 3 proxies para metadados antes de fallback - metadata = execute_with_proxy_retry( - metadata_opts, - extract_metadata_operation, - retry_per_proxy=1, - max_proxies_to_try=3 - ) title = metadata.get("title", unique_id) clean_title = sanitize_title(title) @@ -244,43 +197,12 @@ def download_video( "cached": True } - # ETAPA 2: Download COM PROXY (tentativa rápida) - def download_with_proxy(ydl): + with YoutubeDL(download_opts) as ydl: result = ydl.extract_info(target, download=True) - return result - download_success = False - result = None - - try: - # Tenta apenas 2 proxies para download antes de fallback - result = execute_with_proxy_retry( - download_opts, - download_with_proxy, - retry_per_proxy=1, - max_proxies_to_try=2 - ) - download_success = True - except ProxyError as proxy_err: - # ETAPA 3: Download SEM PROXY (fallback) - - # Aumenta timeout para download sem proxy - download_opts_no_proxy = {**download_opts, "socket_timeout": 180} - - try: - with YoutubeDL(download_opts_no_proxy) as ydl: - result = ydl.extract_info(target, download=True) - download_success = True - except Exception as e: - raise HTTPException( - status_code=500, - detail=f"Falha no download com e sem proxy: {str(e)}" - ) - - if not download_success or not result: + if not result: raise HTTPException(status_code=500, detail="Erro desconhecido no download") - - # Renomear arquivo para nome final + if "requested_downloads" in result and len(result["requested_downloads"]) > 0: real_file_path = result["requested_downloads"][0]["filepath"] elif "filepath" in result: @@ -301,8 +223,6 @@ def download_video( except HTTPException: raise - except ProxyError as e: - raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") except Exception as e: raise HTTPException(status_code=500, detail=f"Erro ao baixar vídeo: {e}") @@ -323,7 +243,7 @@ def search_youtube_yt_dlp( search_query = f"ytsearch{max_results}:{q}" try: - def search_operation(ydl): + with YoutubeDL(ydl_opts) as ydl: search_result = ydl.extract_info(search_query, download=False) entries = search_result.get("entries", [])[:max_results] @@ -339,49 +259,32 @@ def search_youtube_yt_dlp( }) return {"results": results} - return execute_with_proxy_retry(ydl_opts, search_operation, retry_per_proxy=1, max_proxies_to_try=3) - - except ProxyError as e: - raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") except Exception as e: raise HTTPException(status_code=500, detail=f"Erro ao buscar vídeos: {e}") @app.get("/list-formats") def list_formats(url: str): opts = { - "quiet": False, - "no_warnings": False, - "noplaylist": True, + "quiet": True, + "skip_download": True, "nocheckcertificate": True, - "force_ipv4": True, - "geo_bypass": True, - "extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}}, - "http_headers": { - "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", - "User-Agent": "com.google.android.youtube/19.17.36 (Linux; U; Android 13) gzip", - }, - "socket_timeout": 8, - "retries": 0, + "no_check_certificates": True, } try: - def list_formats_operation(ydl): + with YoutubeDL(opts) as ydl: info = ydl.extract_info(url, download=False) fmts = info.get("formats") or [] brief = [{ "id": f.get("format_id"), "ext": f.get("ext"), - "h": f.get("height"), + "height": f.get("height"), "fps": f.get("fps"), - "v": f.get("vcodec"), - "a": f.get("acodec"), + "vcodec": f.get("vcodec"), + "acodec": f.get("acodec"), "tbr": f.get("tbr"), } for f in fmts] return {"total": len(brief), "formats": brief[:60]} - return execute_with_proxy_retry(opts, list_formats_operation, retry_per_proxy=1, max_proxies_to_try=3) - - except ProxyError as e: - raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") except Exception as e: raise HTTPException(status_code=500, detail=f"Erro ao listar formatos: {e}") diff --git a/proxy_manager.py b/proxy_manager.py deleted file mode 100644 index 230294e..0000000 --- a/proxy_manager.py +++ /dev/null @@ -1,129 +0,0 @@ -from typing import Callable, Any, Optional -from yt_dlp import YoutubeDL -from database import get_all_active_proxies, format_proxy_url, mark_proxy_success, mark_proxy_failure - -class ProxyError(Exception): - pass - -def is_proxy_error(error_msg: str) -> bool: - proxy_error_keywords = [ - 'proxy', - 'connection', - 'timeout', - 'timed out', - 'refused', - 'unreachable', - 'unable to connect', - 'network', - 'failed to connect', - 'connection reset', - 'connection aborted', - 'read timed out', - 'http error 407', # Proxy authentication required - 'tunnel connection failed', - 'connect to host', - 'bot', # YouTube bloqueando proxy como bot - 'sign in to confirm', # YouTube pedindo verificação - 'http error 429', # Too many requests - 'failed to extract', # Falha ao extrair dados (pode ser proxy ruim) - 'player response', # Problemas com resposta do player - 'http error 403', # Forbidden (pode ser proxy bloqueado) - 'http error 503', # Service unavailable (pode ser proxy) - 'ssl', # Erros SSL causados por proxy - 'certificate', # Erros de certificado causados por proxy - 'certificate_verify_failed', # Verificação de certificado SSL falhou - 'failed to parse json', # Erros de parsing JSON (proxy retornando HTML) - 'jsondecode', # Erros de decodificação JSON - 'remote end closed', # Conexão fechada pelo proxy - 'remotedisconnected', # Desconexão remota - 'connection/parsing error', # Erros de conexão/parsing do download - ] - - non_proxy_error_keywords = [ - 'requested format is not available', - 'format not available', - 'no video formats', - 'video unavailable', - 'private video', - 'age restricted', - ] - - error_lower = error_msg.lower() - - if any(keyword in error_lower for keyword in non_proxy_error_keywords): - return False - - return any(keyword in error_lower for keyword in proxy_error_keywords) - -def execute_with_proxy_retry( - ydl_opts: dict, - operation: Callable[[YoutubeDL], Any], - retry_per_proxy: int = 2, - max_proxies_to_try: int = None -) -> Any: - """ - Tenta executar operação com proxies disponíveis. - Cada proxy é tentado N vezes antes de passar para o próximo. - Proxies NÃO são removidos do banco, apenas pulados. - - Args: - ydl_opts: Opções do YoutubeDL - operation: Função a ser executada - retry_per_proxy: Número de tentativas por proxy antes de pular para o próximo - max_proxies_to_try: Número máximo de proxies a tentar (None = todos) - """ - # Busca TODOS os proxies ativos - all_proxies = get_all_active_proxies() - - # Limita número de proxies se especificado - if max_proxies_to_try is not None and max_proxies_to_try > 0: - all_proxies = all_proxies[:max_proxies_to_try] - - total_proxies = len(all_proxies) - - last_error = None - - # Tenta cada proxy da lista - for proxy_index, proxy_data in enumerate(all_proxies, 1): - proxy_url = format_proxy_url(proxy_data) - # Proxy desativado temporariamente - Para reativar, descomente a linha abaixo: - # ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url} - ydl_opts_with_proxy = {**ydl_opts} # Sem proxy por enquanto - - proxy_failed = False - - # Tenta N vezes com o MESMO proxy - for attempt in range(1, retry_per_proxy + 1): - try: - with YoutubeDL(ydl_opts_with_proxy) as ydl: - result = operation(ydl) - - mark_proxy_success(proxy_data['id']) - return result - - except Exception as e: - error_msg = str(e) - last_error = e - - # Se não for erro de proxy, lança imediatamente - if not is_proxy_error(error_msg): - raise e - - proxy_failed = True - - # Se chegou aqui, falhou todas as tentativas com este proxy - if proxy_failed: - mark_proxy_failure(proxy_data['id']) - - # Se chegou aqui, todos os proxies falharam, tenta SEM proxy - try: - with YoutubeDL(ydl_opts) as ydl: - result = operation(ydl) - return result - except Exception as e: - last_error = e - - raise ProxyError( - f"Falha após tentar {total_proxies} proxies + tentativa sem proxy. Último erro: {last_error}" - ) - diff --git a/setup_proxies_table.sql b/setup_proxies_table.sql deleted file mode 100644 index cc1a1b9..0000000 --- a/setup_proxies_table.sql +++ /dev/null @@ -1,55 +0,0 @@ --- Script para criar a tabela de proxies --- Execute este script no seu banco de dados PostgreSQL - -CREATE TABLE IF NOT EXISTS proxies ( - id SERIAL PRIMARY KEY, - ip_address VARCHAR(255) NOT NULL, - port INTEGER NOT NULL, - protocol VARCHAR(10) NOT NULL DEFAULT 'http', - username VARCHAR(255), - password VARCHAR(255), - country_code VARCHAR(10), - country_name VARCHAR(100), - city VARCHAR(100), - is_active BOOLEAN DEFAULT TRUE, - is_anonymous BOOLEAN DEFAULT FALSE, - response_time_ms INTEGER, - last_checked_at TIMESTAMP, - last_successful_at TIMESTAMP, - failure_count INTEGER DEFAULT 0, - success_count INTEGER DEFAULT 0, - usage VARCHAR(50), - source VARCHAR(100), - notes TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT check_protocol CHECK (protocol IN ('http', 'https', 'socks5', 'socks4')) -); - --- Índices para melhorar performance -CREATE INDEX IF NOT EXISTS idx_proxies_is_active ON proxies(is_active); -CREATE INDEX IF NOT EXISTS idx_proxies_last_successful_at ON proxies(last_successful_at DESC NULLS LAST); -CREATE INDEX IF NOT EXISTS idx_proxies_response_time ON proxies(response_time_ms ASC NULLS LAST); -CREATE INDEX IF NOT EXISTS idx_proxies_created_at ON proxies(created_at DESC); - --- Trigger para atualizar updated_at automaticamente -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = NOW(); - RETURN NEW; -END; -$$ language 'plpgsql'; - -CREATE TRIGGER update_proxies_updated_at BEFORE UPDATE ON proxies -FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); - --- Exemplo de inserção de proxies (remova ou ajuste conforme necessário) --- INSERT INTO proxies (ip_address, port, protocol, is_active, is_anonymous) VALUES --- ('123.456.789.10', 8080, 'http', TRUE, FALSE), --- ('98.765.432.10', 3128, 'https', TRUE, TRUE), --- ('45.67.89.100', 1080, 'socks5', TRUE, TRUE); - --- Exemplo com autenticação --- INSERT INTO proxies (ip_address, port, protocol, username, password, is_active) VALUES --- ('proxy.example.com', 8080, 'http', 'user123', 'pass456', TRUE);