lots of progress

This commit is contained in:
2025-03-31 23:25:59 +03:00
parent 5ef903b195
commit 7d6f72d31c
19 changed files with 1163 additions and 667 deletions
+85 -56
View File
@@ -4,6 +4,7 @@ namespace App\Services;
use App\Models\Proxy;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Exception\RequestException;
class ProxyService
@@ -17,6 +18,13 @@ class ProxyService
'timeout' => 30,
'verify' => false,
'http_errors' => false,
'cookies' => new CookieJar(), // Enables cookies
'allow_redirects' => [
'max' => 10, // Allow up to 10 redirects
'strict' => false,
'referer' => true,
'track_redirects' => true
],
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
@@ -44,6 +52,9 @@ class ProxyService
public function fetchUrl($url)
{
try {
$url = $this->replaceUrl($url);
// Normalize and encode the URL
$url = $this->normalizeUrl($url);
@@ -69,20 +80,31 @@ class ProxyService
$parsedUrl = parse_url($url);
$host = $parsedUrl['host'];
$ip = gethostbyname($host);
if ($ip === $host) {
throw new \Exception("Could not resolve hostname: $host");
}
// Replace hostname with IP in URL
$url = str_replace($host, $ip, $url);
// Add Host header to maintain virtual hosting
$response = $this->client->get($url, [
'headers' => [
'Host' => $host
'Host' => $host,
'Accept-Encoding' => 'identity' // Request uncompressed data
]
]);
$finalUrl = $response->getHeaderLine('X-Guzzle-Redirect-History') ?: $url;
// If redirected to a YouTube consent page, modify the request
if (strpos($finalUrl, 'consent.youtube.com') !== false) {
\Log::info('YouTube consent page detected, bypassing...');
$url = str_replace('consent.youtube.com', 'www.youtube.com', $finalUrl);
$response = $this->client->get($url);
}
} catch (\Exception $dnsError) {
\Log::error('DNS resolution failed', [
'url' => $url,
@@ -176,7 +198,7 @@ class ProxyService
return $this->processCssContent($content, $baseUrl);
}
// Process stylesheet links that don't start with http or https
// Process stylesheet links first
$content = preg_replace_callback(
'/<link[^>]*rel=["\']stylesheet["\'][^>]*href=["\'](?!https?:\/\/)([^"\']+)["\']/i',
function($matches) use ($baseUrl) {
@@ -189,63 +211,50 @@ class ProxyService
// Otherwise, append to the base URL
$url = rtrim($baseUrl, '/') . '/' . ltrim($url, '/');
}
// Log the transformation
\Log::info('Stylesheet URL transformed', [
'original' => $matches[0],
'transformed' => $url
]);
return str_replace($matches[1], $url, $matches[0]);
},
$content
);
// For HTML content, process only anchor tags with external URLs
$patterns = [
// External links in href attributes (only for anchor tags)
'/(<a[^>]*href)=["\'](https?:\/\/[^"\']+)["\']/i'
];
// Process external links (only anchor tags)
$content = preg_replace_callback(
'/(<a[^>]*href)=["\'](https?:\/\/[^"\']+)["\']/i',
function($matches) use ($baseUrl) {
$url = $matches[2];
foreach ($patterns as $pattern) {
$content = preg_replace_callback(
$pattern,
function($matches) use ($baseUrl) {
$url = $matches[2];
// Skip URLs that are already going through our proxy
if (strpos($url, route('proxy.browse')) === 0) {
return $matches[0];
}
// Skip URLs that are already going through our proxy
if (strpos($url, route('proxy.browse')) === 0) {
return $matches[0];
}
// Skip URLs that are already proxied
if (strpos($url, route('proxy.browse')) === 0) {
return $matches[0];
}
// Skip URLs from the same domain
if (strpos($url, parse_url($baseUrl, PHP_URL_HOST)) !== false) {
return $matches[0];
}
// Proxy all http/https URLs
if (preg_match('/^https?:\/\//i', $url)) {
$url = route('proxy.browse') . '?url=' . base64_encode($url);
return $matches[1] . '="' . htmlspecialchars($url) . '"';
}
// Proxy external URLs
$encodedUrl = base64_encode($url);
$proxiedUrl = route('proxy.browse') . '?url=' . $encodedUrl;
// Only proxy external URLs
if (strpos($url, 'http') === 0 && strpos($url, parse_url($baseUrl, PHP_URL_HOST)) === false) {
$url = route('proxy.browse') . '?url=' . base64_encode($url);
}
// Log the URL transformation
\Log::info('URL transformed', [
'original' => $matches[0],
'transformed' => $proxiedUrl
]);
// Log the URL transformation
\Log::info('URL transformed', [
'original' => $matches[0],
'transformed' => $url
]);
return $matches[1] . '="' . htmlspecialchars($proxiedUrl) . '"';
},
$content
);
return $matches[1] . '="' . htmlspecialchars($url) . '"';
},
$content
);
}
// Add JavaScript to handle dynamic links and resources
// Add JavaScript to handle dynamic content
$content = $this->addLinkHandlerScript($content);
return $content;
@@ -359,21 +368,31 @@ class ProxyService
document.addEventListener('DOMContentLoaded', function() {
// Function to encode URL using base64
function encodeUrl(url) {
// First encode the URL to handle special characters
var encoded = encodeURIComponent(url);
// Then convert to base64
return btoa(encoded);
return btoa(encodeURIComponent(url));
}
// Function to convert relative URL to absolute
function toAbsoluteUrl(url) {
if (!url) return url;
if (url.startsWith('http')) return url;
if (url.startsWith('//')) return window.location.protocol + url;
if (url.startsWith('/')) return window.location.origin + url;
return window.location.origin + '/' + url;
}
// Handle dynamically added links
// Function to check if URL should be proxied
function shouldProxyUrl(url) {
if (!url) return false;
if (url.startsWith('{{ route('proxy.browse') }}')) return false;
if (url.startsWith('data:')) return false;
if (url.startsWith('javascript:')) return false;
if (url.startsWith('mailto:')) return false;
if (url.startsWith('tel:')) return false;
if (url.includes('youtube.com') && !url.includes('watch?')) return false; // Allow YouTube navigation
return url.startsWith('http') && !url.includes(window.location.hostname);
}
// Handle clicks on anchor tags
document.addEventListener('click', function(e) {
var target = e.target;
while (target && target.tagName !== 'A') {
@@ -383,7 +402,7 @@ document.addEventListener('DOMContentLoaded', function() {
var href = target.getAttribute('href');
if (href) {
href = toAbsoluteUrl(href);
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
if (shouldProxyUrl(href)) {
e.preventDefault();
var encodedUrl = encodeUrl(href);
window.location.href = '{{ route('proxy.browse') }}?url=' + encodedUrl;
@@ -398,7 +417,7 @@ document.addEventListener('DOMContentLoaded', function() {
var action = form.getAttribute('action');
if (action) {
action = toAbsoluteUrl(action);
if (action.startsWith('http') && !action.includes(window.location.hostname)) {
if (shouldProxyUrl(action)) {
e.preventDefault();
var encodedUrl = encodeUrl(action);
form.action = '{{ route('proxy.browse') }}?url=' + encodedUrl;
@@ -413,7 +432,7 @@ document.addEventListener('DOMContentLoaded', function() {
var href = link.getAttribute('href');
if (href) {
href = toAbsoluteUrl(href);
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
if (shouldProxyUrl(href)) {
var encodedUrl = encodeUrl(href);
link.setAttribute('href', '{{ route('proxy.browse') }}?url=' + encodedUrl);
}
@@ -429,12 +448,11 @@ document.addEventListener('DOMContentLoaded', function() {
mutations.forEach(function(mutation) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1) { // Element node
// Only process anchor tags
if (node.tagName === 'A') {
var href = node.getAttribute('href');
if (href) {
href = toAbsoluteUrl(href);
if (href.startsWith('http') && !href.includes(window.location.hostname)) {
if (shouldProxyUrl(href)) {
var encodedUrl = encodeUrl(href);
node.setAttribute('href', '{{ route('proxy.browse') }}?url=' + encodedUrl);
}
@@ -445,7 +463,7 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
// Start observing the document with the configured parameters
// Start observing the document
observer.observe(document.documentElement, { childList: true, subtree: true });
});
</script>
@@ -456,4 +474,15 @@ EOT;
return $content;
}
private function replaceUrl($url)
{
$parsedUrl = parse_url($url);
if ($parsedUrl['host'] === 'www.youtube.com') {
$url = str_replace('www.youtube.com', 'tube.cadence.moe', $url);
}
return $url;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Services;
class Utils
{
public static function proxifyUrl(string $url, string $siteBaseUrl)
{
if (!self::isNotAbsoluteUrl($url)) {
// should append site url
$url = $siteBaseUrl.$url;
}
return route('proxy.browse') . '?url=' . base64_encode($url);
}
public static function isNotAbsoluteUrl($url) {
return parse_url($url, PHP_URL_SCHEME) !== null;
}
public static function isAbsoluteUrl($url) {
return parse_url($url, PHP_URL_SCHEME) === null;
}
}