From a97966c1b452315e30a58ad3740ba9d1a16e769d Mon Sep 17 00:00:00 2001 From: Renan Bernordi Date: Mon, 6 Jan 2025 19:44:02 -0300 Subject: [PATCH] =?UTF-8?q?tradu=C3=A7=C3=B5es=20de=20documenta=C3=A7?= =?UTF-8?q?=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 + app/api.php | 59 +++++--- app/data/blocked_domains.php | 26 ++-- app/data/domain_rules.php | 34 +++-- app/data/global_rules.php | 11 ++ app/data/user_agents.php | 13 ++ app/inc/Cache.php | 43 ++++-- app/inc/Cache/CacheStorageInterface.php | 24 +++- app/inc/Cache/DiskStorage.php | 30 +++- app/inc/Cache/RedisStorage.php | 54 ++++++- app/inc/Cache/S3Storage.php | 48 +++++-- app/inc/Language.php | 41 ++++++ app/inc/Logger.php | 49 +++++-- app/inc/Rules.php | 49 +++++-- app/inc/URLAnalyzer.php | 179 +++++++++++++----------- app/p.php | 31 +++- 16 files changed, 515 insertions(+), 182 deletions(-) diff --git a/Dockerfile b/Dockerfile index df42f44..531d877 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ FROM php:8.3-fpm +# Install PHP dependencies and extensions # Instala dependências e extensões do PHP RUN apt-get update && apt-get install -y \ nginx \ @@ -14,12 +15,15 @@ RUN apt-get update && apt-get install -y \ && pecl install redis \ && docker-php-ext-enable redis opcache +# Copy OPCache configuration # Copia a configuração do OPCache COPY opcache.ini /usr/local/etc/php/conf.d/opcache.ini +# Install Composer # Instala o Composer RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +# Copy webservice configuration # Copia a configuração do webservice COPY default.conf /etc/nginx/sites-available/default @@ -29,12 +33,14 @@ COPY app/ /app/ WORKDIR /app RUN composer install --no-interaction --optimize-autoloader +# Copy and configure initialization script permissions # Copia e configura permissões do script de inicialização COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh RUN mkdir -p /app/cache /app/logs +# Configure base permissions for /app directory # Configura permissões base para o diretório /app RUN chown -R www-data:www-data /app \ && chmod -R 755 /app diff --git a/app/api.php b/app/api.php index ca31f2e..151146c 100644 --- a/app/api.php +++ b/app/api.php @@ -1,28 +1,40 @@ [ 'code' => 'INVALID_URL', - 'message' => MESSAGES['INVALID_URL']['message'] + 'message' => $errorMessage['message'] ] ], 400); } try { + // Instantiate URL analyzer // Instancia o analisador de URLs $analyzer = new URLAnalyzer(); + // Try to analyze the provided URL // Tenta analisar a URL fornecida $analyzer->analyze($url); + // If analysis is successful, return the processed URL // Se a análise for bem-sucedida, retorna a URL processada sendResponse([ 'url' => SITE_URL . '/p/' . $url ], 200); } catch (Exception $e) { + // Error handling with mapping to appropriate HTTP codes // Tratamento de erros com mapeamento para códigos HTTP apropriados $message = $e->getMessage(); $statusCode = 400; $errorCode = 'GENERIC_ERROR'; - - // Mapeia a mensagem de erro para o código e status apropriados - foreach (MESSAGES as $key => $value) { - if (strpos($message, $value['message']) !== false) { - $statusCode = ($value['type'] === 'error') ? 400 : 503; - $errorCode = $key; + $errorMessage = Language::getMessage('GENERIC_ERROR'); + + // Try to match the error message with known error types + // Tenta corresponder a mensagem de erro com tipos de erro conhecidos + $errorTypes = ['BLOCKED_DOMAIN', 'DNS_FAILURE', 'HTTP_ERROR', 'CONNECTION_ERROR', 'CONTENT_ERROR']; + foreach ($errorTypes as $type) { + $typeMessage = Language::getMessage($type); + if (strpos($message, $typeMessage['message']) !== false) { + $statusCode = ($typeMessage['type'] === 'error') ? 400 : 503; + $errorCode = $type; + $errorMessage = $typeMessage; break; } } + // Add error header for better client-side handling // Adiciona header de erro para melhor tratamento no cliente header('X-Error-Message: ' . $message); sendResponse([ 'error' => [ 'code' => $errorCode, - 'message' => $message + 'message' => $errorMessage['message'] ] ], $statusCode); } } else { + // Return 404 error for endpoints not found // Retorna erro 404 para endpoints não encontrados + $errorMessage = Language::getMessage('NOT_FOUND'); sendResponse([ 'error' => [ 'code' => 'NOT_FOUND', - 'message' => MESSAGES['NOT_FOUND']['message'] + 'message' => $errorMessage['message'] ] ], 404); } diff --git a/app/data/blocked_domains.php b/app/data/blocked_domains.php index 4e89b89..6f8613b 100644 --- a/app/data/blocked_domains.php +++ b/app/data/blocked_domains.php @@ -1,14 +1,18 @@ [ - * 'scriptTagRemove' => ['gtm.js', 'ga.js'], // Exclui scripts específicos das regras globais - * 'classElementRemove' => ['subscription'] // Exclui classes específicas das regras globais + * 'scriptTagRemove' => ['gtm.js', 'ga.js'], // Excludes specific scripts from global rules / Exclui scripts específicos das regras globais + * 'classElementRemove' => ['subscription'] // Excludes specific classes from global rules / Exclui classes específicas das regras globais * ] - * - useSelenium: Boolean indicando se deve usar Selenium para extração + * - useSelenium: Boolean indicating whether to use Selenium for extraction / Boolean indicando se deve usar Selenium para extração */ return [ 'nsctotal.com.br' => [ diff --git a/app/data/global_rules.php b/app/data/global_rules.php index 27ec044..e18c4e7 100644 --- a/app/data/global_rules.php +++ b/app/data/global_rules.php @@ -1,15 +1,24 @@ [ 'subscription', 'subscriber-content', @@ -31,6 +40,8 @@ 'signup-overlay', 'onesignal-slidedown-container' ], + // Scripts to be removed from all pages + // Scripts a serem removidos de todas as páginas 'scriptTagRemove' => [ 'gtm.js', 'ga.js', diff --git a/app/data/user_agents.php b/app/data/user_agents.php index 1641135..ac8f8c1 100644 --- a/app/data/user_agents.php +++ b/app/data/user_agents.php @@ -1,12 +1,25 @@ redisStorage = new RedisStorage(CACHE_DIR); + // If S3 is configured and active, use S3Storage // Se S3 está configurado e ativo, usa S3Storage if (defined('S3_CACHE_ENABLED') && S3_CACHE_ENABLED === true) { $this->storage = new S3Storage([ @@ -47,15 +59,17 @@ public function __construct() 'endpoint' => defined('S3_ENDPOINT') ? S3_ENDPOINT : null ]); } else { + // Otherwise, use disk storage // Caso contrário, usa o storage em disco $this->storage = new DiskStorage(CACHE_DIR); } } /** + * Gets the count of cached files * Obtém a contagem de arquivos em cache * - * @return int Número de arquivos em cache + * @return int Number of files in cache / Número de arquivos em cache */ public function getCacheFileCount(): int { @@ -63,27 +77,32 @@ public function getCacheFileCount(): int } /** + * Generates a unique ID for a URL * Gera um ID único para uma URL * - * @param string $url URL para qual será gerado o ID - * @return string Hash SHA-256 da URL normalizada + * @param string $url URL for which the ID will be generated / URL para qual será gerado o ID + * @return string SHA-256 hash of the normalized URL / Hash SHA-256 da URL normalizada */ public function generateId($url) { + // Remove protocol and www // Remove protocolo e www $url = preg_replace('#^https?://(www\.)?#', '', $url); + // Generate unique ID using SHA-256 // Gera ID único usando SHA-256 return hash('sha256', $url); } /** + * Checks if cache exists for a given URL * Verifica se existe cache para uma determinada URL * - * @param string $url URL a ser verificada - * @return bool True se existir cache, False caso contrário + * @param string $url URL to check / URL a ser verificada + * @return bool True if cache exists, False otherwise / True se existir cache, False caso contrário */ public function exists($url) { + // If DISABLE_CACHE is active, always return false // Se DISABLE_CACHE está ativo, sempre retorna false if (DISABLE_CACHE) { return false; @@ -93,13 +112,15 @@ public function exists($url) } /** + * Retrieves cached content for a URL * Recupera o conteúdo em cache de uma URL * - * @param string $url URL do conteúdo a ser recuperado - * @return string|null Conteúdo em cache ou null se não existir + * @param string $url URL of the content to retrieve / URL do conteúdo a ser recuperado + * @return string|null Cached content or null if it doesn't exist / Conteúdo em cache ou null se não existir */ public function get($url) { + // If DISABLE_CACHE is active, always return null // Se DISABLE_CACHE está ativo, sempre retorna null if (DISABLE_CACHE) { return null; @@ -109,14 +130,16 @@ public function get($url) } /** + * Stores content in cache for a URL * Armazena conteúdo em cache para uma URL * - * @param string $url URL associada ao conteúdo - * @param string $content Conteúdo a ser armazenado em cache - * @return bool True se o cache foi salvo com sucesso, False caso contrário + * @param string $url URL associated with the content / URL associada ao conteúdo + * @param string $content Content to be cached / Conteúdo a ser armazenado em cache + * @return bool True if cache was saved successfully, False otherwise / True se o cache foi salvo com sucesso, False caso contrário */ public function set($url, $content) { + // If DISABLE_CACHE is active, don't generate cache // Se DISABLE_CACHE está ativo, não gera cache if (DISABLE_CACHE) { return true; diff --git a/app/inc/Cache/CacheStorageInterface.php b/app/inc/Cache/CacheStorageInterface.php index 89f2d2a..2d73b09 100644 --- a/app/inc/Cache/CacheStorageInterface.php +++ b/app/inc/Cache/CacheStorageInterface.php @@ -2,30 +2,40 @@ namespace Inc\Cache; +/** + * Interface for cache storage implementations + * Interface para implementações de armazenamento de cache + * + * This interface defines the required methods for any cache storage implementation. + * Esta interface define os métodos necessários para qualquer implementação de armazenamento de cache. + */ interface CacheStorageInterface { /** + * Checks if cache exists for a given ID * Verifica se existe cache para um determinado ID * - * @param string $id ID do cache - * @return bool + * @param string $id Cache ID / ID do cache + * @return bool True if cache exists, false otherwise / True se o cache existir, false caso contrário */ public function exists(string $id): bool; /** + * Retrieves cached content * Recupera o conteúdo em cache * - * @param string $id ID do cache - * @return string|null + * @param string $id Cache ID / ID do cache + * @return string|null Cached content or null if not found / Conteúdo em cache ou null se não encontrado */ public function get(string $id): ?string; /** + * Stores content in cache * Armazena conteúdo em cache * - * @param string $id ID do cache - * @param string $content Conteúdo a ser armazenado - * @return bool + * @param string $id Cache ID / ID do cache + * @param string $content Content to be stored / Conteúdo a ser armazenado + * @return bool True if successful, false otherwise / True se bem sucedido, false caso contrário */ public function set(string $id, string $content): bool; } diff --git a/app/inc/Cache/DiskStorage.php b/app/inc/Cache/DiskStorage.php index 8b8cbe7..2a635e3 100644 --- a/app/inc/Cache/DiskStorage.php +++ b/app/inc/Cache/DiskStorage.php @@ -2,17 +2,26 @@ namespace Inc\Cache; +/** + * Disk-based cache storage implementation + * Implementação de armazenamento de cache em disco + * + * This class implements file-based caching using gzip compression + * Esta classe implementa cache baseado em arquivos usando compressão gzip + */ class DiskStorage implements CacheStorageInterface { /** + * @var string Directory where cache files will be stored * @var string Diretório onde os arquivos de cache serão armazenados */ private $cacheDir; /** + * Class constructor * Construtor da classe * - * @param string $cacheDir Diretório base para armazenamento do cache + * @param string $cacheDir Base directory for cache storage / Diretório base para armazenamento do cache */ public function __construct(string $cacheDir) { @@ -23,7 +32,11 @@ public function __construct(string $cacheDir) } /** - * {@inheritdoc} + * Checks if cache exists for a given ID + * Verifica se existe cache para um determinado ID + * + * @param string $id Cache ID / ID do cache + * @return bool True if cache exists, false otherwise / True se o cache existir, false caso contrário */ public function exists(string $id): bool { @@ -32,7 +45,11 @@ public function exists(string $id): bool } /** - * {@inheritdoc} + * Retrieves cached content + * Recupera o conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @return string|null Cached content or null if not found / Conteúdo em cache ou null se não encontrado */ public function get(string $id): ?string { @@ -51,7 +68,12 @@ public function get(string $id): ?string } /** - * {@inheritdoc} + * Stores content in cache + * Armazena conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @param string $content Content to be stored / Conteúdo a ser armazenado + * @return bool True if successful, false otherwise / True se bem sucedido, false caso contrário */ public function set(string $id, string $content): bool { diff --git a/app/inc/Cache/RedisStorage.php b/app/inc/Cache/RedisStorage.php index 7576f04..988d970 100644 --- a/app/inc/Cache/RedisStorage.php +++ b/app/inc/Cache/RedisStorage.php @@ -2,60 +2,82 @@ namespace Inc\Cache; +/** + * Redis-based cache storage implementation + * Implementação de armazenamento de cache baseado em Redis + * + * This class provides cache storage and file counting functionality using Redis + * Esta classe fornece armazenamento de cache e funcionalidade de contagem de arquivos usando Redis + * + * @property \Redis|null $redis Redis client instance + */ class RedisStorage implements CacheStorageInterface { /** * @var \Redis|null Redis client instance + * @var \Redis|null Instância do cliente Redis */ private $redis; /** + * @var string Cache directory for file counting * @var string Diretório de cache para contagem de arquivos */ private $cacheDir; /** + * Class constructor * Construtor da classe * - * @param string $cacheDir Diretório base para armazenamento do cache + * @param string $cacheDir Base directory for cache storage / Diretório base para armazenamento do cache */ public function __construct(string $cacheDir) { $this->cacheDir = $cacheDir; + // Try to initialize Redis connection // Tenta inicializar conexão Redis try { + /** @var \Redis $redis */ $this->redis = new \Redis(); $this->redis->connect(REDIS_HOST, REDIS_PORT, 2.5); $this->redis->setOption(\Redis::OPT_PREFIX, REDIS_PREFIX); } catch (\Exception $e) { + // If it fails, set redis to null // Se falhar, define redis como null $this->redis = null; } } /** + * Counts the number of files in the cache directory * Conta o número de arquivos no diretório de cache * - * @return int Número de arquivos no diretório de cache + * @return int Number of files in the cache directory / Número de arquivos no diretório de cache */ public function countCacheFiles(): int { + // Key to store file count in Redis // Chave para armazenar a contagem de arquivos no Redis $cacheCountKey = 'cache_file_count'; + // If Redis is available // Se Redis estiver disponível if ($this->redis !== null) { + // Check if the key exists and has a value // Verifica se a chave existe e tem valor + /** @var string|false $cachedCount */ $cachedCount = $this->redis->get($cacheCountKey); if ($cachedCount !== false) { return (int)$cachedCount; } } + // If Redis is not available or key is empty, count files // Se Redis não estiver disponível ou chave vazia, conta os arquivos $fileCount = iterator_count(new \FilesystemIterator($this->cacheDir)); + // If Redis is available, save the count // Se Redis estiver disponível, salva a contagem if ($this->redis !== null) { $this->redis->set($cacheCountKey, $fileCount); @@ -65,9 +87,10 @@ public function countCacheFiles(): int } /** + * Updates the file count in Redis * Atualiza a contagem de arquivos no Redis * - * @param int $count Número de arquivos + * @param int $count Number of files / Número de arquivos */ public function updateCacheFileCount(int $count): void { @@ -77,7 +100,11 @@ public function updateCacheFileCount(int $count): void } /** - * {@inheritdoc} + * Checks if cache exists for a given ID + * Verifica se existe cache para um determinado ID + * + * @param string $id Cache ID / ID do cache + * @return bool True if cache exists, false otherwise / True se o cache existir, false caso contrário */ public function exists(string $id): bool { @@ -85,7 +112,11 @@ public function exists(string $id): bool } /** - * {@inheritdoc} + * Retrieves cached content + * Recupera o conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @return string|null Cached content or null if not found / Conteúdo em cache ou null se não encontrado */ public function get(string $id): ?string { @@ -93,25 +124,36 @@ public function get(string $id): ?string return null; } + /** @var string|false $content */ $content = $this->redis->get($id); return $content === false ? null : $content; } /** - * {@inheritdoc} + * Stores content in cache + * Armazena conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @param string $content Content to be stored / Conteúdo a ser armazenado + * @return bool True if successful, false otherwise / True se bem sucedido, false caso contrário */ public function set(string $id, string $content): bool { + // If Redis is not available, return false // Se Redis não estiver disponível, retorna false if ($this->redis === null) { return false; } + // When saving a new file, update the count // Ao salvar um novo arquivo, atualiza a contagem + /** @var bool $result */ $result = $this->redis->set($id, $content); if ($result) { + // Increment file count in Redis // Incrementa a contagem de arquivos no Redis + /** @var string|false $currentCount */ $currentCount = $this->redis->get('cache_file_count') ?: 0; $this->redis->set('cache_file_count', $currentCount + 1); } diff --git a/app/inc/Cache/S3Storage.php b/app/inc/Cache/S3Storage.php index a965bfe..051c848 100644 --- a/app/inc/Cache/S3Storage.php +++ b/app/inc/Cache/S3Storage.php @@ -5,32 +5,44 @@ use Aws\S3\S3Client; use Aws\Exception\AwsException; +/** + * AWS S3-based cache storage implementation + * Implementação de armazenamento de cache baseado em AWS S3 + * + * This class provides cache storage functionality using Amazon S3 or compatible services + * Esta classe fornece funcionalidade de armazenamento de cache usando Amazon S3 ou serviços compatíveis + */ class S3Storage implements CacheStorageInterface { /** + * @var S3Client AWS S3 Client * @var S3Client Cliente AWS S3 */ private $s3Client; /** + * @var string S3 bucket name * @var string Nome do bucket S3 */ private $bucket; /** + * @var string Prefix for objects in the bucket (optional) * @var string Prefixo para os objetos no bucket (opcional) */ private $prefix; /** + * @var string ACL for S3 objects * @var string ACL para os objetos no S3 */ private $acl; /** + * Class constructor * Construtor da classe * - * @param array $config Configuração do AWS S3 + * @param array $config AWS S3 configuration / Configuração do AWS S3 */ public function __construct(array $config) { @@ -43,10 +55,12 @@ public function __construct(array $config) ] ]; + // Add custom endpoint if provided // Adiciona endpoint personalizado se fornecido if (!empty($config['endpoint'])) { $clientConfig['endpoint'] = $config['endpoint']; - // Use path-style endpoints quando um endpoint personalizado é fornecido + // Use path-style endpoints when a custom endpoint is provided + // Use endpoints estilo path quando um endpoint personalizado é fornecido $clientConfig['use_path_style_endpoint'] = true; } @@ -58,10 +72,11 @@ public function __construct(array $config) } /** + * Generates the complete object key in S3 * Gera a chave completa do objeto no S3 * - * @param string $id ID do cache - * @return string + * @param string $id Cache ID / ID do cache + * @return string Complete S3 object key / Chave completa do objeto no S3 */ private function getObjectKey(string $id): string { @@ -69,7 +84,11 @@ private function getObjectKey(string $id): string } /** - * {@inheritdoc} + * Checks if cache exists for a given ID + * Verifica se existe cache para um determinado ID + * + * @param string $id Cache ID / ID do cache + * @return bool True if cache exists, false otherwise / True se o cache existir, false caso contrário */ public function exists(string $id): bool { @@ -79,13 +98,17 @@ public function exists(string $id): bool $this->getObjectKey($id) ); } catch (AwsException $e) { - // Log error if needed + // Log error if needed / Registra erro se necessário return false; } } /** - * {@inheritdoc} + * Retrieves cached content + * Recupera o conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @return string|null Cached content or null if not found / Conteúdo em cache ou null se não encontrado */ public function get(string $id): ?string { @@ -112,7 +135,12 @@ public function get(string $id): ?string } /** - * {@inheritdoc} + * Stores content in cache + * Armazena conteúdo em cache + * + * @param string $id Cache ID / ID do cache + * @param string $content Content to be stored / Conteúdo a ser armazenado + * @return bool True if successful, false otherwise / True se bem sucedido, false caso contrário */ public function set(string $id, string $content): bool { @@ -128,12 +156,12 @@ public function set(string $id, string $content): bool 'Body' => $compressedContent, 'ACL' => $this->acl, 'ContentEncoding' => 'gzip', - 'CacheControl' => 'max-age=31536000' // 1 year + 'CacheControl' => 'max-age=31536000' // 1 year / 1 ano ]); return true; } catch (AwsException $e) { - // Log error if needed + // Log error if needed / Registra erro se necessário return false; } } diff --git a/app/inc/Language.php b/app/inc/Language.php index d571a41..831c3df 100644 --- a/app/inc/Language.php +++ b/app/inc/Language.php @@ -1,9 +1,28 @@ 'Unknown message', @@ -28,6 +63,12 @@ public static function getMessage($key) { ]; } + /** + * Get current language code + * Obtém o código do idioma atual + * + * @return string Current language code / Código do idioma atual + */ public static function getCurrentLanguage() { return self::$currentLanguage; } diff --git a/app/inc/Logger.php b/app/inc/Logger.php index 619368f..d89883a 100644 --- a/app/inc/Logger.php +++ b/app/inc/Logger.php @@ -5,18 +5,46 @@ use Exception; use \Hawk\Catcher; +/** + * Error logging and monitoring class + * Classe de monitoramento e registro de erros + * + * This class implements error logging functionality using Hawk.so + * for monitoring and tracking application errors. + * + * Esta classe implementa funcionalidades de registro de erros usando Hawk.so + * para monitoramento e rastreamento de erros da aplicação. + */ class Logger { + /** + * @var Logger|null Singleton instance + * @var Logger|null Instância singleton + */ private static $instance = null; + /** + * Private constructor to prevent direct instantiation + * Construtor privado para prevenir instanciação direta + * + * Initializes Hawk monitoring + * Inicializa o monitoramento Hawk + */ private function __construct() { + // Initialize Hawk // Inicializa o Hawk Catcher::init([ 'integrationToken' => HAWK_TOKEN, ]); } + /** + * Gets singleton instance + * Obtém instância singleton + * + * @return Logger Instance of Logger / Instância do Logger + */ public static function getInstance(): Logger { if (self::$instance === null) { @@ -26,14 +54,16 @@ public static function getInstance(): Logger } /** + * Logs an error with context * Registra um erro com contexto * - * @param string $message Mensagem de erro - * @param array $context Dados adicionais de contexto - * @param string $type Tipo/categoria do erro + * @param string $message Error message / Mensagem de erro + * @param array $context Additional context data / Dados adicionais de contexto + * @param string $type Error type/category / Tipo/categoria do erro */ public function error(string $message, array $context = [], string $type = 'WARNING'): void { + // Log to Hawk // Registra no Hawk try { Catcher::get()->sendException(new Exception($message), [ @@ -41,21 +71,24 @@ public function error(string $message, array $context = [], string $type = 'WARN 'context' => $context ]); } catch (Exception $e) { + // If Hawk fails, we already have file logging as backup // Se o Hawk falhar, já temos o log em arquivo como backup - error_log("Falha ao enviar erro para o Hawk: " . $e->getMessage()); + error_log("Failed to send error to Hawk / Falha ao enviar erro para o Hawk: " . $e->getMessage()); } } /** + * Logs a URL-specific error * Registra um erro específico de URL * - * @param string $url A URL que gerou o erro - * @param string $error_group Grupo/categoria do erro - * @param string $message_error Detalhes adicionais do erro - * @param string $type Tipo/categoria do erro + * @param string $url The URL that generated the error / A URL que gerou o erro + * @param string $error_group Error group/category / Grupo/categoria do erro + * @param string $message_error Additional error details / Detalhes adicionais do erro + * @param string $type Error type/category / Tipo/categoria do erro */ public function log(string $url, string $error_group, string $message_error = '', string $type = 'WARNING'): void { + // If no Hawk token is configured, don't generate log // Se não houver token do Hawk configurado, não gera log if (empty(HAWK_TOKEN)) { return; diff --git a/app/inc/Rules.php b/app/inc/Rules.php index 2c584ab..88f925f 100644 --- a/app/inc/Rules.php +++ b/app/inc/Rules.php @@ -1,8 +1,14 @@ getGlobalRules(); } /** + * Merges domain-specific rules with global rules * Mescla regras específicas do domínio com regras globais * - * @param array $rules Regras específicas do domínio - * @return array Regras mescladas + * @param array $rules Domain-specific rules / Regras específicas do domínio + * @return array Merged rules / Regras mescladas */ private function mergeWithGlobalRules($rules) { $globalRules = $this->getGlobalRules(); $mergedRules = []; + // Process global rules exclusions // Processa as exclusões de regras globais $excludeGlobalRules = isset($rules['excludeGlobalRules']) ? $rules['excludeGlobalRules'] : []; - unset($rules['excludeGlobalRules']); // Remove do array de regras para não ser processado como regra normal + unset($rules['excludeGlobalRules']); // Remove from rules array to avoid processing as normal rule / Remove do array de regras para não ser processado como regra normal + // First, add all global rules except excluded ones // Primeiro, adiciona todas as regras globais, exceto as excluídas foreach ($globalRules as $ruleType => $globalTypeRules) { if (!in_array($ruleType, $this->supportedRuleTypes)) { @@ -124,10 +144,12 @@ private function mergeWithGlobalRules($rules) } if (isset($excludeGlobalRules[$ruleType])) { + // If rule type is an associative array (like cookies or headers) // Se o tipo de regra é um array associativo (como cookies ou headers) if (is_array($globalTypeRules) && array_keys($globalTypeRules) !== range(0, count($globalTypeRules) - 1)) { $mergedRules[$ruleType] = array_diff_key($globalTypeRules, array_flip($excludeGlobalRules[$ruleType])); } else { + // For simple arrays (like classElementRemove) // Para arrays simples (como classElementRemove) $mergedRules[$ruleType] = array_diff($globalTypeRules, $excludeGlobalRules[$ruleType]); } @@ -136,6 +158,7 @@ private function mergeWithGlobalRules($rules) } } + // Then, merge with domain-specific rules // Depois, mescla com as regras específicas do domínio foreach ($rules as $ruleType => $domainTypeRules) { if (!in_array($ruleType, $this->supportedRuleTypes)) { @@ -147,11 +170,14 @@ private function mergeWithGlobalRules($rules) continue; } + // If rule type already exists, merge appropriately // Se o tipo de regra já existe, mescla apropriadamente if (in_array($ruleType, ['cookies', 'headers'])) { + // For cookies and headers, preserve keys // Para cookies e headers, preserva as chaves $mergedRules[$ruleType] = array_merge($mergedRules[$ruleType], $domainTypeRules); } else { + // For other types, merge as simple arrays // Para outros tipos, mescla como arrays simples $mergedRules[$ruleType] = array_values(array_unique(array_merge( $mergedRules[$ruleType], @@ -164,9 +190,10 @@ private function mergeWithGlobalRules($rules) } /** + * Returns all global rules * Retorna todas as regras globais * - * @return array Array com todas as regras globais + * @return array Array with all global rules / Array com todas as regras globais */ public function getGlobalRules() { diff --git a/app/inc/URLAnalyzer.php b/app/inc/URLAnalyzer.php index 1166667..9399324 100644 --- a/app/inc/URLAnalyzer.php +++ b/app/inc/URLAnalyzer.php @@ -1,16 +1,19 @@ activatedRules = []; - // 1. Limpa a URL + // 1. Clean URL / Limpa a URL $cleanUrl = $this->cleanUrl($url); if (!$cleanUrl) { - throw new Exception("URL inválida"); + throw new Exception(Language::getMessage('INVALID_URL')['message']); } - // 2. Verifica cache + // 2. Check cache / Verifica cache if ($this->cache->exists($cleanUrl)) { return $this->cache->get($cleanUrl); } - // 3. Verifica domínios bloqueados + // 3. Check blocked domains / Verifica domínios bloqueados $host = parse_url($cleanUrl, PHP_URL_HOST); $host = preg_replace('/^www\./', '', $host); if (in_array($host, BLOCKED_DOMAINS)) { - throw new Exception('Este domínio está bloqueado para extração.'); + throw new Exception(Language::getMessage('BLOCKED_DOMAIN')['message']); } - // 4. Verifica se deve usar Selenium + // 4. Check if should use Selenium / Verifica se deve usar Selenium $domainRules = $this->getDomainRules($host); if (isset($domainRules['useSelenium']) && $domainRules['useSelenium'] === true) { try { @@ -137,13 +151,12 @@ public function analyze($url) return $processedContent; } } catch (Exception $e) { - $error = 'SELENIUM_ERROR'; Logger::getInstance()->log($cleanUrl, 'SELENIUM_ERROR', $e->getMessage()); - throw new Exception($error); + throw new Exception(Language::getMessage('CONTENT_ERROR')['message']); } } - // 5. Tenta buscar conteúdo diretamente + // 5. Try to fetch content directly / Tenta buscar conteúdo diretamente try { $content = $this->fetchContent($cleanUrl); if (!empty($content)) { @@ -155,7 +168,7 @@ public function analyze($url) error_log("DIRECT_FETCH_ERROR: " . $e->getMessage()); } - // 6. Tenta buscar do Wayback Machine como fallback + // 6. Try to fetch from Wayback Machine as fallback / Tenta buscar do Wayback Machine como fallback try { $content = $this->fetchFromWaybackMachine($cleanUrl); if (!empty($content)) { @@ -167,7 +180,7 @@ public function analyze($url) error_log("WAYBACK_FETCH_ERROR: " . $e->getMessage()); } - // 7. Tenta buscar com Selenium como fallback + // 7. Try to fetch with Selenium as fallback / Tenta buscar com Selenium como fallback try { $content = $this->fetchFromSelenium($cleanUrl, 'firefox'); if (!empty($content)) { @@ -180,16 +193,17 @@ public function analyze($url) } Logger::getInstance()->log($cleanUrl, 'GENERAL_FETCH_ERROR'); - throw new Exception("Não foi possível obter o conteúdo da URL"); + throw new Exception(Language::getMessage('CONTENT_ERROR')['message']); } /** + * Try to get content using Selenium * Tenta obter o conteúdo da URL usando Selenium * - * @param string $url URL para buscar - * @param array $domainRules Regras específicas do domínio - * @return string|null Conteúdo HTML da página - * @throws Exception Em caso de erro na requisição + * @param string $url URL to fetch / URL para buscar + * @param string $browser Browser to use (chrome/firefox) / Navegador a ser usado (chrome/firefox) + * @return string|null HTML content of the page / Conteúdo HTML da página + * @throws Exception In case of request error / Em caso de erro na requisição */ private function fetchFromSelenium($url, $browser) { @@ -210,12 +224,12 @@ private function fetchFromSelenium($url, $browser) $capabilities->setCapability(ChromeOptions::CAPABILITY, $options); } else { $profile = new FirefoxProfile(); - $profile->setPreference("permissions.default.image", 2); // Não carrega imagens - $profile->setPreference("javascript.enabled", true); // Mantem habilitado javascripts - $profile->setPreference("network.http.referer.defaultPolicy", 0); // Sempre envia referer - $profile->setPreference("network.http.referer.defaultReferer", "https://www.google.com.br"); // Define referer padrão - $profile->setPreference("network.http.referer.spoofSource", true); // Permite spoofing do referer - $profile->setPreference("network.http.referer.trimmingPolicy", 0); // Não corta o referer + $profile->setPreference("permissions.default.image", 2); // Don't load images / Não carrega imagens + $profile->setPreference("javascript.enabled", true); // Keep JavaScript enabled / Mantém JavaScript habilitado + $profile->setPreference("network.http.referer.defaultPolicy", 0); // Always send referer / Sempre envia referer + $profile->setPreference("network.http.referer.defaultReferer", "https://www.google.com.br"); // Set default referer / Define referer padrão + $profile->setPreference("network.http.referer.spoofSource", true); // Allow referer spoofing / Permite spoofing do referer + $profile->setPreference("network.http.referer.trimmingPolicy", 0); // Don't trim referer / Não corta o referer $options = new FirefoxOptions(); $options->setProfile($profile); @@ -232,12 +246,11 @@ private function fetchFromSelenium($url, $browser) $driver->get($url); $htmlSource = $driver->executeScript("return document.documentElement.outerHTML;"); - //$htmlSource = $driver->getPageSource(); $driver->quit(); if (empty($htmlSource)) { - throw new Exception("Selenium returned empty content"); + throw new Exception("Selenium returned empty content / Selenium retornou conteúdo vazio"); } return $htmlSource; @@ -250,11 +263,12 @@ private function fetchFromSelenium($url, $browser) } /** + * Try to get content from Internet Archive's Wayback Machine * Tenta obter o conteúdo da URL do Internet Archive's Wayback Machine * - * @param string $url URL original - * @return string|null Conteúdo do arquivo - * @throws Exception Em caso de erro na requisição + * @param string $url Original URL / URL original + * @return string|null Archive content / Conteúdo do arquivo + * @throws Exception In case of request error / Em caso de erro na requisição */ private function fetchFromWaybackMachine($url) { @@ -270,12 +284,12 @@ private function fetchFromWaybackMachine($url) $curl->get($availabilityUrl); if ($curl->error || $curl->httpStatusCode !== 200) { - throw new Exception("Erro ao verificar disponibilidade no Wayback Machine"); + throw new Exception(Language::getMessage('HTTP_ERROR')['message']); } $data = $curl->response; if (!isset($data->archived_snapshots->closest->url)) { - throw new Exception("Nenhum snapshot encontrado no Wayback Machine"); + throw new Exception(Language::getMessage('CONTENT_ERROR')['message']); } $archiveUrl = $data->archived_snapshots->closest->url; @@ -288,11 +302,12 @@ private function fetchFromWaybackMachine($url) $curl->get($archiveUrl); if ($curl->error || $curl->httpStatusCode !== 200 || empty($curl->response)) { - throw new Exception("Erro ao obter conteúdo do Wayback Machine"); + throw new Exception(Language::getMessage('HTTP_ERROR')['message']); } $content = $curl->response; + // Remove Wayback Machine toolbar and cache URLs // Remove o toolbar do Wayback Machine e URLs de cache $content = preg_replace('/.*?/s', '', $content); $content = preg_replace('/https?:\/\/web\.archive\.org\/web\/\d+im_\//', '', $content); @@ -301,11 +316,12 @@ private function fetchFromWaybackMachine($url) } /** + * Perform HTTP request using Curl Class * Realiza requisição HTTP usando Curl Class * - * @param string $url URL para requisição - * @return string Conteúdo da página - * @throws Exception Em caso de erro na requisição + * @param string $url URL for request / URL para requisição + * @return string Page content / Conteúdo da página + * @throws Exception In case of request error / Em caso de erro na requisição */ private function fetchContent($url) { @@ -320,13 +336,13 @@ private function fetchContent($url) $curl->setOpt(CURLOPT_SSL_VERIFYPEER, false); $curl->setOpt(CURLOPT_DNS_SERVERS, implode(',', $this->dnsServers)); - // Define User Agent + // Set User Agent / Define User Agent $userAgent = isset($domainRules['userAgent']) ? $domainRules['userAgent'] : $this->userAgents[array_rand($this->userAgents)]; $curl->setUserAgent($userAgent); - // Headers padrão + // Default headers / Headers padrão $headers = [ 'Host' => $host, 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', @@ -335,13 +351,13 @@ private function fetchContent($url) 'Pragma' => 'no-cache' ]; - // Adiciona headers específicos do domínio + // Add domain-specific headers / Adiciona headers específicos do domínio if (isset($domainRules['headers'])) { $headers = array_merge($headers, $domainRules['headers']); } $curl->setHeaders($headers); - // Adiciona cookies específicos do domínio + // Add domain-specific cookies / Adiciona cookies específicos do domínio if (isset($domainRules['cookies'])) { $cookies = []; foreach ($domainRules['cookies'] as $name => $value) { @@ -354,7 +370,7 @@ private function fetchContent($url) } } - // Adiciona referer se especificado + // Add referer if specified / Adiciona referer se especificado if (isset($domainRules['referer'])) { $curl->setHeader('Referer', $domainRules['referer']); } @@ -362,43 +378,44 @@ private function fetchContent($url) $curl->get($url); if ($curl->error || $curl->httpStatusCode !== 200) { - throw new Exception("Erro HTTP " . $curl->httpStatusCode . ": " . $curl->errorMessage); + throw new Exception(Language::getMessage('HTTP_ERROR')['message']); } if (empty($curl->response)) { - throw new Exception("Resposta vazia do servidor"); + throw new Exception(Language::getMessage('CONTENT_ERROR')['message']); } return $curl->response; } /** + * Clean and normalize a URL * Limpa e normaliza uma URL * - * @param string $url URL para limpar - * @return string|false URL limpa e normalizada ou false se inválida + * @param string $url URL to clean / URL para limpar + * @return string|false Cleaned and normalized URL or false if invalid / URL limpa e normalizada ou false se inválida */ private function cleanUrl($url) { $url = trim($url); - // Verifica se a URL é válida + // Check if URL is valid / Verifica se a URL é válida if (!filter_var($url, FILTER_VALIDATE_URL)) { return false; } - // Detecta e converte URLs AMP + // Detect and convert AMP URLs / Detecta e converte URLs AMP if (preg_match('#https://([^.]+)\.cdn\.ampproject\.org/v/s/([^/]+)(.*)#', $url, $matches)) { $url = 'https://' . $matches[2] . $matches[3]; } - // Separa a URL em suas partes componentes + // Split URL into component parts / Separa a URL em suas partes componentes $parts = parse_url($url); - // Reconstrói a URL base + // Rebuild base URL / Reconstrói a URL base $cleanedUrl = $parts['scheme'] . '://' . $parts['host']; - // Adiciona o caminho se existir + // Add path if exists / Adiciona o caminho se existir if (isset($parts['path'])) { $cleanedUrl .= $parts['path']; } @@ -407,10 +424,11 @@ private function cleanUrl($url) } /** + * Get specific rules for a domain * Obtém regras específicas para um domínio * - * @param string $domain Domínio para buscar regras - * @return array|null Regras do domínio ou null se não encontrar + * @param string $domain Domain to search rules / Domínio para buscar regras + * @return array|null Domain rules or null if not found / Regras do domínio ou null se não encontrar */ private function getDomainRules($domain) { @@ -418,10 +436,11 @@ private function getDomainRules($domain) } /** + * Remove specific classes from an element * Remove classes específicas de um elemento * - * @param DOMElement $element Elemento DOM - * @param array $classesToRemove Classes a serem removidas + * @param DOMElement $element DOM Element / Elemento DOM + * @param array $classesToRemove Classes to remove / Classes a serem removidas */ private function removeClassNames($element, $classesToRemove) { @@ -442,11 +461,12 @@ private function removeClassNames($element, $classesToRemove) } /** + * Fix relative URLs in a DOM document * Corrige URLs relativas em um documento DOM * - * @param DOMDocument $dom Documento DOM - * @param DOMXPath $xpath Objeto XPath - * @param string $baseUrl URL base para correção + * @param DOMDocument $dom DOM Document / Documento DOM + * @param DOMXPath $xpath XPath Object / Objeto XPath + * @param string $baseUrl Base URL for correction / URL base para correção */ private function fixRelativeUrls($dom, $xpath, $baseUrl) { @@ -490,12 +510,13 @@ private function fixRelativeUrls($dom, $xpath, $baseUrl) } /** + * Process HTML content applying domain rules * Processa o conteúdo HTML aplicando regras do domínio * - * @param string $content Conteúdo HTML - * @param string $host Nome do host - * @param string $url URL completa - * @return string Conteúdo processado + * @param string $content HTML content / Conteúdo HTML + * @param string $host Host name / Nome do host + * @param string $url Complete URL / URL completa + * @return string Processed content / Conteúdo processado */ private function processContent($content, $host, $url) { @@ -507,17 +528,17 @@ private function processContent($content, $host, $url) $xpath = new DOMXPath($dom); - // Processa tags canônicas + // Process canonical tags / Processa tags canônicas $canonicalLinks = $xpath->query("//link[@rel='canonical']"); if ($canonicalLinks !== false) { - // Remove todas as tags canônicas existentes foreach ($canonicalLinks as $link) { if ($link->parentNode) { $link->parentNode->removeChild($link); } } } - // Adiciona nova tag canônica com a URL original + + // Add new canonical tag with original URL / Adiciona nova tag canônica com a URL original $head = $xpath->query('//head')->item(0); if ($head) { $newCanonical = $dom->createElement('link'); @@ -526,7 +547,7 @@ private function processContent($content, $host, $url) $head->appendChild($newCanonical); } - // Sempre aplica a correção de URLs relativas + // Always fix relative URLs / Sempre corrige URLs relativas $this->fixRelativeUrls($dom, $xpath, $url); $domainRules = $this->getDomainRules($host); @@ -586,6 +607,7 @@ private function processContent($content, $host, $url) if (isset($domainRules['scriptTagRemove'])) { foreach ($domainRules['scriptTagRemove'] as $script) { + // Search for script tags with src or content containing the script // Busca por tags script com src ou conteúdo contendo o script $scriptElements = $xpath->query("//script[contains(@src, '$script')] | //script[contains(text(), '$script')]"); if ($scriptElements !== false && $scriptElements->length > 0) { @@ -597,6 +619,7 @@ private function processContent($content, $host, $url) $this->activatedRules[] = "scriptTagRemove: $script"; } + // Search for link tags that are scripts // Busca por tags link que são scripts $linkElements = $xpath->query("//link[@as='script' and contains(@href, '$script') and @type='application/javascript']"); if ($linkElements !== false && $linkElements->length > 0) { @@ -621,7 +644,7 @@ private function processContent($content, $host, $url) } } - // Adiciona CTA Marreta + // Add Marreta CTA / Adiciona CTA Marreta $body = $xpath->query('//body')->item(0); if ($body) { $marretaDiv = $dom->createElement('div'); @@ -631,14 +654,14 @@ private function processContent($content, $host, $url) $marretaDiv->appendChild($marretaHtml); $body->appendChild($marretaDiv); - // Adiciona painel de debug se DEBUG for true + // Add debug panel if DEBUG is true / Adiciona painel de debug se DEBUG for true if (DEBUG) { $debugDiv = $dom->createElement('div'); $debugDiv->setAttribute('style', 'z-index: 99999; position: fixed; bottom: 10px; right: 10px; background: rgba(0,0,0,0.8); color: #fff; font-size: 13px; line-height: 1.4; padding: 10px; border-radius: 3px; font-family: monospace; max-height: 200px; overflow-y: auto;'); - if( empty($this->activatedRules)) { + if (empty($this->activatedRules)) { $ruleElement = $dom->createElement('div'); - $ruleElement->textContent = 'Nenhuma regra ativada'; + $ruleElement->textContent = 'No rules activated / Nenhuma regra ativada'; $debugDiv->appendChild($ruleElement); } else { foreach ($this->activatedRules as $rule) { diff --git a/app/p.php b/app/p.php index 101d58d..b7249bf 100644 --- a/app/p.php +++ b/app/p.php @@ -1,55 +1,73 @@ checkRedirects($url); + // If there's a redirect and the final URL is different // Se houver redirecionamento e a URL final for diferente if ($redirectInfo['hasRedirect'] && $redirectInfo['finalUrl'] !== $url) { + // Redirect to final URL // Redireciona para a URL final header('Location: ' . SITE_URL . '/p/' . urlencode($redirectInfo['finalUrl'])); exit; } + // If there's no redirect or if already at final URL // Se não houver redirecionamento ou se já estiver na URL final + // Try to analyze and process the URL // Tenta analisar e processar a URL $content = $analyzer->analyze($url); + // Display processed content // Exibe o conteúdo processado echo $content; exit; } catch (Exception $e) { $errorMessage = $e->getMessage(); - $errorType = 'GENERIC_ERROR'; // Tipo padrão de erro + $errorType = 'GENERIC_ERROR'; // Default error type / Tipo padrão de erro + // Map error message to specific type // Mapeia a mensagem de erro para um tipo específico - if (strpos($errorMessage, 'bloqueado') !== false) { + if (strpos($errorMessage, 'bloqueado') !== false || strpos($errorMessage, 'blocked') !== false) { $errorType = 'BLOCKED_DOMAIN'; } elseif (strpos($errorMessage, 'DNS') !== false) { $errorType = 'DNS_FAILURE'; @@ -57,21 +75,22 @@ $errorType = 'HTTP_ERROR'; } elseif (strpos($errorMessage, 'CURL') !== false) { $errorType = 'CONNECTION_ERROR'; - } elseif (strpos($errorMessage, 'obter conteúdo') !== false) { + } elseif (strpos($errorMessage, 'obter conteúdo') !== false || strpos($errorMessage, 'get content') !== false) { $errorType = 'CONTENT_ERROR'; } + // Redirect to home page with error message // Redireciona para a página inicial com mensagem de erro header('Location: /?message=' . $errorType); exit; } } else { - // URL inválida + // Invalid URL / URL inválida header('Location: /?message=INVALID_URL'); exit; } } else { - // Path inválido + // Invalid path / Path inválido header('Location: /?message=NOT_FOUND'); exit; }