From bc06a7cbc924cd73103cd8aca6f352e63c6d66ac Mon Sep 17 00:00:00 2001 From: Renan Bernordi Date: Fri, 24 Jan 2025 00:19:52 -0300 Subject: [PATCH] =?UTF-8?q?implementado=20fastroute=20para=20controle=20e?= =?UTF-8?q?=20seguran=C3=A7a=20de=20rotas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api.php | 133 ------------------- app/composer.json | 6 +- app/inc/URLAnalyzer.php | 16 +-- app/index.php | 217 ++----------------------------- app/p.php | 95 -------------- app/pwa.php | 81 ------------ app/src/Router.php | 179 +++++++++++++++++++++++++ app/src/api.php | 119 +++++++++++++++++ app/src/p.php | 88 +++++++++++++ app/src/views/home.php | 157 ++++++++++++++++++++++ app/{ => src/views}/manifest.php | 4 +- default.conf | 14 +- 12 files changed, 570 insertions(+), 539 deletions(-) delete mode 100644 app/api.php delete mode 100644 app/p.php delete mode 100644 app/pwa.php create mode 100644 app/src/Router.php create mode 100644 app/src/api.php create mode 100644 app/src/p.php create mode 100644 app/src/views/home.php rename app/{ => src/views}/manifest.php (94%) diff --git a/app/api.php b/app/api.php deleted file mode 100644 index 6a5d112..0000000 --- a/app/api.php +++ /dev/null @@ -1,133 +0,0 @@ - $statusCode - ]; - - if (isset($data['error'])) { - $response['error'] = $data['error']; - } else if (isset($data['url'])) { - $response['url'] = $data['url']; - } - - echo json_encode($response); - exit; - } - - // Basic URL validation - // Validação básica da URL - if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) { - sendResponse([ - 'error' => [ - 'type' => URLAnalyzer::ERROR_INVALID_URL, - 'message' => Language::getMessage('INVALID_URL')['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 (URLAnalyzerException $e) { - // Get error details from the exception - // Obtém detalhes do erro da exceção - $errorType = $e->getErrorType(); - $additionalInfo = $e->getAdditionalInfo(); - - // Add error header for better client-side handling - // Adiciona header de erro para melhor tratamento no cliente - header('X-Error-Type: ' . $errorType); - if ($additionalInfo) { - header('X-Error-Info: ' . $additionalInfo); - } - - sendResponse([ - 'error' => [ - 'type' => $errorType, - 'message' => $e->getMessage(), - 'details' => $additionalInfo ?: null - ] - ], $e->getCode()); - } catch (Exception $e) { - // Handle any other unexpected errors - // Trata quaisquer outros erros inesperados - sendResponse([ - 'error' => [ - 'type' => URLAnalyzer::ERROR_GENERIC_ERROR, - 'message' => Language::getMessage('GENERIC_ERROR')['message'] - ] - ], 500); - } -} else { - // Return 404 error for endpoints not found - // Retorna erro 404 para endpoints não encontrados - sendResponse([ - 'error' => [ - 'type' => URLAnalyzer::ERROR_NOT_FOUND, - 'message' => Language::getMessage('NOT_FOUND')['message'] - ] - ], 404); -} diff --git a/app/composer.json b/app/composer.json index ea65703..d9b51dc 100644 --- a/app/composer.json +++ b/app/composer.json @@ -4,11 +4,13 @@ "aws/aws-sdk-php": "^3.0", "php-curl-class/php-curl-class": "^11.0", "php-webdriver/webdriver": "^1.15", - "monolog/monolog": "^3.8.1" + "monolog/monolog": "^3.8.1", + "nikic/fast-route": "^1.3" }, "autoload": { "psr-4": { - "Inc\\": "inc/" + "Inc\\": "inc/", + "App\\": "src/" } } } diff --git a/app/inc/URLAnalyzer.php b/app/inc/URLAnalyzer.php index 4a70842..e8e0fe2 100644 --- a/app/inc/URLAnalyzer.php +++ b/app/inc/URLAnalyzer.php @@ -16,10 +16,10 @@ * - Selenium extraction support when enabled by domain / Suporte a extração via Selenium quando habilitado por domínio */ -require_once 'Rules.php'; -require_once 'Cache.php'; -require_once 'Logger.php'; -require_once 'Language.php'; +require_once __DIR__ . '/Rules.php'; +require_once __DIR__ . '/Cache.php'; +require_once __DIR__ . '/Logger.php'; +require_once __DIR__ . '/Language.php'; use Curl\Curl; use Facebook\WebDriver\Remote\DesiredCapabilities; @@ -253,14 +253,14 @@ class URLAnalyzer $host = preg_replace('/^www\./', '', $host); if (in_array($host, BLOCKED_DOMAINS)) { - Logger::getInstance()->log($cleanUrl, 'BLOCKED_DOMAIN'); + Logger::getInstance()->logUrl($cleanUrl, 'BLOCKED_DOMAIN'); $this->throwError(self::ERROR_BLOCKED_DOMAIN); } // Check URL status code before proceeding $redirectInfo = $this->checkStatus($cleanUrl); if ($redirectInfo['httpCode'] !== 200) { - Logger::getInstance()->log($cleanUrl, 'INVALID_STATUS_CODE', "HTTP {$redirectInfo['httpCode']}"); + Logger::getInstance()->logUrl($cleanUrl, 'INVALID_STATUS_CODE', "HTTP {$redirectInfo['httpCode']}"); if ($redirectInfo['httpCode'] === 404) { $this->throwError(self::ERROR_NOT_FOUND); } else { @@ -296,7 +296,7 @@ class URLAnalyzer return $processedContent; } } catch (Exception $e) { - Logger::getInstance()->log($cleanUrl, strtoupper($fetchStrategy) . '_ERROR', $e->getMessage()); + Logger::getInstance()->logUrl($cleanUrl, strtoupper($fetchStrategy) . '_ERROR', $e->getMessage()); throw $e; } } @@ -326,7 +326,7 @@ class URLAnalyzer } // If we get here, all strategies failed - Logger::getInstance()->log($cleanUrl, 'GENERAL_FETCH_ERROR'); + Logger::getInstance()->logUrl($cleanUrl, 'GENERAL_FETCH_ERROR'); if ($lastError) { $message = $lastError->getMessage(); if (strpos($message, 'DNS') !== false) { diff --git a/app/index.php b/app/index.php index 1ad200d..4dfa5a0 100644 --- a/app/index.php +++ b/app/index.php @@ -1,214 +1,17 @@ getCacheFileCount(); -?> - - - - - - - <?php echo SITE_NAME; ?> - - - - - - - - - - - - - - - - - - -
- -
-

- Marreta - -

-

-

- - - - -

-
- - -
-
-
-
- - Link - - -
- -
-
- - -
-

-
- - - -
-
-
- - Error - - Warning - -
-
-

- -

-
-
-
- -
- - -
-

- App - -

-
-

- -

-
-
    -
  1. -
  2. -
  3. -
  4. -
-
-
-
- - - - -
-

- -

-

- API RestGithub -

-
- -
- - - - - +$router = new App\Router(); +$router->dispatch(); diff --git a/app/p.php b/app/p.php deleted file mode 100644 index cb5affd..0000000 --- a/app/p.php +++ /dev/null @@ -1,95 +0,0 @@ -checkStatus($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 (URLAnalyzerException $e) { - // Get error type and additional info from exception - // Obtém o tipo de erro e informações adicionais da exceção - $errorType = $e->getErrorType(); - $additionalInfo = $e->getAdditionalInfo(); - - // Handle blocked domain with redirect URL - // Trata domínio bloqueado com URL de redirecionamento - if ($errorType === URLAnalyzer::ERROR_BLOCKED_DOMAIN && $additionalInfo) { - header('Location: ' . trim($additionalInfo) . '?message=' . $errorType); - exit; - } - - // Redirect to home page with error message - // Redireciona para a página inicial com mensagem de erro - header('Location: /?message=' . $errorType); - exit; - } catch (Exception $e) { - // Handle any other unexpected errors - // Trata quaisquer outros erros inesperados - header('Location: /?message=' . URLAnalyzer::ERROR_GENERIC_ERROR); - exit; - } - } else { - // Invalid URL / URL inválida - header('Location: /?message=' . URLAnalyzer::ERROR_INVALID_URL); - exit; - } -} else { - // Invalid path / Path inválido - header('Location: /?message=' . URLAnalyzer::ERROR_NOT_FOUND); - exit; -} diff --git a/app/pwa.php b/app/pwa.php deleted file mode 100644 index d9f638c..0000000 --- a/app/pwa.php +++ /dev/null @@ -1,81 +0,0 @@ -dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) { + // Rota principal - página inicial + // Main route - home page + $r->addRoute('GET', '/', function() { + // Inicialização das variáveis para a view principal + // Initialize variables for the main view + require_once __DIR__ . '/../config.php'; + require_once __DIR__ . '/../inc/Cache.php'; + require_once __DIR__ . '/../inc/Language.php'; + + \Language::init(LANGUAGE); + + $message = ''; + $message_type = ''; + $url = ''; + + // Processa mensagens da query string + // Process query string messages + if (isset($_GET['message'])) { + $message_key = $_GET['message']; + $messageData = \Language::getMessage($message_key); + $message = $messageData['message']; + $message_type = $messageData['type']; + } + + // Processa submissão do formulário + // Process form submission + if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['url'])) { + $url = filter_var($_POST['url'], FILTER_SANITIZE_URL); + if (filter_var($url, FILTER_VALIDATE_URL)) { + header('Location: ' . SITE_URL . '/p/' . urlencode($url)); + exit; + } else { + $messageData = \Language::getMessage('INVALID_URL'); + $message = $messageData['message']; + $message_type = $messageData['type']; + } + } + + // Inicializa o cache para contagem + // Initialize cache for counting + $cache = new \Cache(); + $cache_folder = $cache->getCacheFileCount(); + + require __DIR__ . '/views/home.php'; + }); + + // Rota da API - inclui api.php existente + // API route - includes existing api.php + $r->addRoute('GET', '/api/{url:.+}', function($vars) { + $_GET['url'] = $vars['url']; + require __DIR__ . '/api.php'; + }); + + // Rota da API sem parâmetros - redireciona para raiz + // API route without parameters - redirects to root + $r->addRoute('GET', '/api[/]', function() { + header('Location: /'); + exit; + }); + + // Rota de processamento - inclui p.php existente + // Processing route - includes existing p.php + $r->addRoute('GET', '/p/{url:.+}', function($vars) { + $_GET['url'] = $vars['url']; + require __DIR__ . '/p.php'; + }); + + // Rota de processamento com query parameter ou sem parâmetros + // Processing route with query parameter or without parameters + $r->addRoute('GET', '/p[/]', function() { + if (isset($_GET['url']) || isset($_GET['text'])) { + $url = isset($_GET['url']) ? $_GET['url'] : ''; + $text = isset($_GET['text']) ? $_GET['text'] : ''; + + // Check which parameter is a valid URL + if (filter_var($url, FILTER_VALIDATE_URL)) { + header('Location: /p/' . urlencode($url)); + exit; + } elseif (filter_var($text, FILTER_VALIDATE_URL)) { + header('Location: /p/' . urlencode($text)); + exit; + } else { + header('Location: /?message=INVALID_URL'); + exit; + } + } + header('Location: /'); + exit; + }); + + // Rota do manifesto PWA - inclui manifest.php existente + // PWA manifest route - includes existing manifest.php + $r->addRoute('GET', '/manifest.json', function() { + require __DIR__ . '/views/manifest.php'; + }); + }); + } + + /** + * Despacha a requisição para a rota apropriada + * Dispatches the request to the appropriate route + */ + public function dispatch() + { + $httpMethod = $_SERVER['REQUEST_METHOD']; + $uri = $_SERVER['REQUEST_URI']; + + // Remove a query string mas mantém para processamento + // Strip query string but keep for processing + $queryString = ''; + if (false !== $pos = strpos($uri, '?')) { + $queryString = substr($uri, $pos); + $uri = substr($uri, 0, $pos); + } + $uri = rawurldecode($uri); + + // Parse query string parameters + if ($queryString) { + parse_str(substr($queryString, 1), $_GET); + } + + $routeInfo = $this->dispatcher->dispatch($httpMethod, $uri); + + switch ($routeInfo[0]) { + case FastRoute\Dispatcher::NOT_FOUND: + require_once __DIR__ . '/../config.php'; + header('Location: ' . SITE_URL); + exit; + + case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: + header("HTTP/1.0 405 Method Not Allowed"); + echo '405 Method Not Allowed'; + break; + + case FastRoute\Dispatcher::FOUND: + $handler = $routeInfo[1]; + $vars = $routeInfo[2]; + call_user_func($handler, $vars); + break; + } + } +} diff --git a/app/src/api.php b/app/src/api.php new file mode 100644 index 0000000..1c48a62 --- /dev/null +++ b/app/src/api.php @@ -0,0 +1,119 @@ + $statusCode + ]; + + if (isset($data['error'])) { + $response['error'] = $data['error']; + } else if (isset($data['url'])) { + $response['url'] = $data['url']; + } + + echo json_encode($response); + exit; +} + +// Get URL from Router +// Obtém a URL do Router +$url = isset($_GET['url']) ? urldecode($_GET['url']) : ''; + +// Basic URL validation +// Validação básica da URL +if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) { + sendResponse([ + 'error' => [ + 'type' => URLAnalyzer::ERROR_INVALID_URL, + 'message' => Language::getMessage('INVALID_URL')['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 (URLAnalyzerException $e) { + // Get error details from the exception + // Obtém detalhes do erro da exceção + $errorType = $e->getErrorType(); + $additionalInfo = $e->getAdditionalInfo(); + + // Add error header for better client-side handling + // Adiciona header de erro para melhor tratamento no cliente + header('X-Error-Type: ' . $errorType); + if ($additionalInfo) { + header('X-Error-Info: ' . $additionalInfo); + } + + sendResponse([ + 'error' => [ + 'type' => $errorType, + 'message' => $e->getMessage(), + 'details' => $additionalInfo ?: null + ] + ], $e->getCode()); +} catch (Exception $e) { + // Handle any other unexpected errors + // Trata quaisquer outros erros inesperados + sendResponse([ + 'error' => [ + 'type' => URLAnalyzer::ERROR_GENERIC_ERROR, + 'message' => Language::getMessage('GENERIC_ERROR')['message'] + ] + ], 500); +} diff --git a/app/src/p.php b/app/src/p.php new file mode 100644 index 0000000..e89da54 --- /dev/null +++ b/app/src/p.php @@ -0,0 +1,88 @@ +checkStatus($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 (URLAnalyzerException $e) { + // Get error type and additional info from exception + // Obtém o tipo de erro e informações adicionais da exceção + $errorType = $e->getErrorType(); + $additionalInfo = $e->getAdditionalInfo(); + + // Handle blocked domain with redirect URL + // Trata domínio bloqueado com URL de redirecionamento + if ($errorType === URLAnalyzer::ERROR_BLOCKED_DOMAIN && $additionalInfo) { + header('Location: ' . trim($additionalInfo) . '?message=' . $errorType); + exit; + } + + // Redirect to home page with error message + // Redireciona para a página inicial com mensagem de erro + header('Location: /?message=' . $errorType); + exit; + } catch (Exception $e) { + // Handle any other unexpected errors + // Trata quaisquer outros erros inesperados + header('Location: /?message=' . URLAnalyzer::ERROR_GENERIC_ERROR); + exit; + } +} else { + // Invalid URL / URL inválida + header('Location: /?message=' . URLAnalyzer::ERROR_INVALID_URL); + exit; +} diff --git a/app/src/views/home.php b/app/src/views/home.php new file mode 100644 index 0000000..9782840 --- /dev/null +++ b/app/src/views/home.php @@ -0,0 +1,157 @@ + + + + + + + <?php echo SITE_NAME; ?> + + + + + + + + + + + + + + + + + + +
+ +
+

+ Marreta + +

+

+

+ + + + +

+
+ + +
+
+
+
+ + Link + + +
+ +
+
+ + +
+

+
+ + + +
+
+
+ + Error + + Warning + +
+
+

+ +

+
+
+
+ +
+ + +
+

+ App + +

+
+

+ +

+
+
    +
  1. +
  2. +
  3. +
  4. +
+
+
+
+ + + + +
+

+ +

+

+ API RestGithub +

+
+ +
+ + + + + \ No newline at end of file diff --git a/app/manifest.php b/app/src/views/manifest.php similarity index 94% rename from app/manifest.php rename to app/src/views/manifest.php index 610477f..dceae66 100644 --- a/app/manifest.php +++ b/app/src/views/manifest.php @@ -9,8 +9,8 @@ * Ele define o comportamento da aplicação quando instalada em um dispositivo e sua aparência. */ -require_once 'config.php'; -require_once 'inc/Language.php'; +require_once __DIR__ . '/../../config.php'; +require_once __DIR__ . '/../../inc/Language.php'; header('Content-Type: application/json'); diff --git a/default.conf b/default.conf index 7f67e4f..24e2932 100644 --- a/default.conf +++ b/default.conf @@ -48,18 +48,10 @@ server { return 403; } + # All requests go through index.php for FastRoute routing + # Todas as requisições passam pelo index.php para roteamento FastRoute location / { - try_files $uri $uri/ $uri/index.php?$args; - } - - location ~ ^/(api|p)/ { - try_files $uri $uri/ /$1.php; - } - - # Serve manifest.json from manifest.php - # Serve manifest.json a partir do manifest.php - location = /manifest.json { - rewrite ^ /manifest.php last; + try_files $uri $uri/ /index.php?$args; } location ~ \.php$ {