diff --git a/app/Http/Controllers/PortScannerController.php b/app/Http/Controllers/PortScannerController.php new file mode 100644 index 0000000..fbf7a7a --- /dev/null +++ b/app/Http/Controllers/PortScannerController.php @@ -0,0 +1,63 @@ +validate([ + 'ip' => 'required|ip', + 'port' => 'required|integer|min:1|max:65535', + ]); + + $ip = $request->input('ip'); + $port = $request->input('port'); + $timeout = 2; // Timeout in seconds + + try { + $startTime = microtime(true); + $connection = @fsockopen($ip, $port, $errno, $errstr, $timeout); + $endTime = microtime(true); + $responseTime = round(($endTime - $startTime) * 1000, 2); // Convert to milliseconds + + if ($connection) { + fclose($connection); + $status = 'open'; + $message = "Port $port is open on $ip"; + } else { + $status = 'closed'; + $message = "Port $port is closed on $ip"; + } + + return response()->json([ + 'success' => true, + 'status' => $status, + 'message' => $message, + 'response_time' => $responseTime, + 'ip' => $ip, + 'port' => $port + ]); + } catch (\Exception $e) { + Log::error('Port scan error', [ + 'ip' => $ip, + 'port' => $port, + 'error' => $e->getMessage() + ]); + + return response()->json([ + 'success' => false, + 'message' => 'An error occurred while scanning the port', + 'error' => $e->getMessage() + ], 500); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ProxyController.php b/app/Http/Controllers/ProxyController.php deleted file mode 100644 index e4f3cc7..0000000 --- a/app/Http/Controllers/ProxyController.php +++ /dev/null @@ -1,93 +0,0 @@ -proxyService = $proxyService; - } - - public function home() - { - return view('proxy.home'); - } - - public function browse(Request $request) - { - $request->validate([ - 'url' => 'required' - ]); - - try { - // Decode the URL if it's base64 encoded - $url = $request->url; - if (base64_encode(base64_decode($url, true)) === $url) { - // Decode base64 and then URL decode - $url = urldecode(base64_decode($url)); - } - - // Log the URL processing - \Log::info('Processing URL', [ - 'original' => $request->url, - 'decoded' => $url - ]); - - // Validate the decoded URL - if (!filter_var($url, FILTER_VALIDATE_URL)) { - throw new \Exception('Invalid URL format: ' . $url); - } - - $content = $this->proxyService->fetchUrl($url); - - // Get content type from response - $contentType = $this->proxyService->getLastContentType(); - - // Log the content type for debugging - \Log::info('Content type received', [ - 'url' => $url, - 'content_type' => $contentType, - 'is_css' => strpos($contentType, 'text/css') !== false || - strpos($contentType, 'application/css') !== false || - strpos($url, '.css') !== false - ]); - - // If it's a CSS file, serve it with the correct content type - if (strpos($contentType, 'text/css') !== false || - strpos($contentType, 'application/css') !== false || - strpos($url, '.css') !== false) { - - \Log::info('Serving CSS file', [ - 'url' => $url, - 'content_type' => $contentType, - 'content_length' => strlen($content) - ]); - - return response($content) - ->header('Content-Type', 'text/css') - ->header('Cache-Control', 'public, max-age=3600') - ->header('Access-Control-Allow-Origin', '*') - ->header('X-Content-Type-Options', 'nosniff'); - } - - // For HTML content, return the view - return view('proxy.browse', [ - 'url' => $url, - 'content' => $content - ]); - } catch (\Exception $e) { - \Log::error('Proxy error', [ - 'url' => $request->url, - 'error' => $e->getMessage(), - 'trace' => $e->getTraceAsString() - ]); - return back()->withErrors(['url' => 'Failed to fetch URL: ' . $e->getMessage()]); - } - } -} diff --git a/app/Http/Controllers/ProxyListController.php b/app/Http/Controllers/ProxyListController.php index 22b9658..205f505 100644 --- a/app/Http/Controllers/ProxyListController.php +++ b/app/Http/Controllers/ProxyListController.php @@ -2,12 +2,17 @@ namespace App\Http\Controllers; +use App\Models\Proxy; use Illuminate\Http\Request; class ProxyListController extends Controller { public function index() { - return view('proxies.index'); + $proxies = Proxy::orderBy('score', 'desc') + ->orderBy('last_tested_at', 'desc') + ->paginate(20); + + return view('proxies.index', compact('proxies')); } } \ No newline at end of file diff --git a/app/Services/ProxyService.php b/app/Services/ProxyService.php index c1e0cbc..6a492fd 100644 --- a/app/Services/ProxyService.php +++ b/app/Services/ProxyService.php @@ -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( '/]*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) - '/(]*href)=["\'](https?:\/\/[^"\']+)["\']/i' - ]; + // Process external links (only anchor tags) + $content = preg_replace_callback( + '/(]*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 }); }); @@ -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; + } } diff --git a/app/Services/Utils.php b/app/Services/Utils.php new file mode 100644 index 0000000..fe5b2ad --- /dev/null +++ b/app/Services/Utils.php @@ -0,0 +1,24 @@ + - +merge(['class' => 'text-primary']) }}> + diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 4024c64..05631ed 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,17 +1,93 @@ - - -

- {{ __('Dashboard') }} -

-
+@extends('layouts.app') -
-
-
-
- {{ __("You're logged in!") }} +@section('title', 'Dashboard - Web Proxy Scanner') +@section('description', 'Access your proxy scanning tools, view recent scans, and manage your account settings from your personalized dashboard.') + +@section('content') +
+
+
+
+
+

+ Welcome back, {{ Auth::user()->name }}! +

+
+ +
+ + + + +
+

Recent Activity

+
+
+

No recent activity to display.

+
+
+
+ + +
+

Account Status

+
+
+ Subscription Status + + Free + +
+
+ Member Since + {{ Auth::user()->created_at->format('M d, Y') }} +
+
+
+
+ + +
+

Getting Started

+
+
+

1. Scan Free Proxies

+

Start by scanning our list of free proxies to find available servers.

+
+
+

2. Use Port Scanner

+

Check port availability and status with our port scanning tool.

+
+
+

3. Upgrade Plan

+

Get access to premium features by upgrading your subscription.

+
+
- +
+@endsection diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 0a471a4..618ef73 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -5,32 +5,110 @@ - {{ config('app.name', 'Laravel') }} + @yield('title', config('app.name')) + + + + + + + + + + + + + + + + + - - + + + - - @vite(['resources/css/app.css', 'resources/js/app.js']) + + + + + + + + - -
+ +
@include('layouts.navigation') - @isset($header) -
-
+ @if (isset($header)) +
+
{{ $header }}
- @endisset + @endif
- {{ $slot }} + @yield('content')
+ + + + + + @vite(['resources/js/app.js']) diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php index acdb926..574870f 100644 --- a/resources/views/layouts/navigation.blade.php +++ b/resources/views/layouts/navigation.blade.php @@ -1,132 +1,75 @@ - diff --git a/resources/views/layouts/proxy.blade.php b/resources/views/layouts/proxy.blade.php index 783e463..ead6e10 100644 --- a/resources/views/layouts/proxy.blade.php +++ b/resources/views/layouts/proxy.blade.php @@ -3,76 +3,161 @@ - Web Proxy - @yield('title') + + + {{ config('app.name', 'Laravel') }} + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) - -
- -