Files
url_tracker_tool/apps/web/src/pages/HomePage.tsx
Andrei 6e41d9874d feat: Add Google Analytics integration and fix anonymous tracking
- Add Google Analytics tracking (G-ZDZ26XYN2P) to frontend
- Create comprehensive analytics utility with event tracking
- Track URL submissions, analysis results, and user authentication
- Add route tracking for SPA navigation
- Fix CORS configuration to support both localhost and production
- Fix home page tracking form to display results instead of auto-redirect
- Add service management scripts for easier deployment
- Update database migrations for enhanced analysis features

Key Features:
- Anonymous and authenticated user tracking
- SSL/SEO/Security analysis event tracking
- Error tracking for debugging
- Page view tracking for SPA routes
- Multi-origin CORS support for development and production
2025-08-23 17:45:01 +00:00

714 lines
26 KiB
TypeScript

/**
* Home Page - Landing page and quick start interface
*/
import { useState } from 'react';
import { trackUrlSubmission, trackAnalysisResult, trackError } from '../utils/analytics';
import {
Box,
Heading,
Text,
Container,
VStack,
HStack,
Card,
CardBody,
CardHeader,
SimpleGrid,
Button,
Badge,
Input,
Icon,
useToast,
useColorModeValue,
Stat,
StatLabel,
StatNumber,
StatHelpText,
FormControl,
FormLabel,
Select,
Switch,
FormHelperText,
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb,
NumberInput,
NumberInputField,
NumberInputStepper,
NumberIncrementStepper,
NumberDecrementStepper,
Textarea,
Collapse,
Divider,
Alert,
AlertIcon,
// StatArrow,
// Flex,
// Stack,
// Image,
// Center,
} from '@chakra-ui/react';
import { Link as RouterLink } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import {
FiTrendingUp,
FiActivity,
FiLock,
FiSearch,
FiShield,
FiUpload,
FiBarChart,
FiZap,
// FiGlobe,
FiArrowRight,
FiCheckCircle,
} from 'react-icons/fi';
import { trackingApi, TrackRequestV2 } from '../services/api';
import { useAuth } from '../contexts/AuthContext';
import { TrackingResults } from '../components/Tracking/TrackingResults';
const trackingSchema = z.object({
url: z.string().min(1, 'URL is required').url('Invalid URL format'),
method: z.enum(['GET', 'POST', 'HEAD']),
userAgent: z.string().optional(),
maxHops: z.number().min(1).max(20),
timeout: z.number().min(1000).max(30000),
enableSSLAnalysis: z.boolean(),
enableSEOAnalysis: z.boolean(),
enableSecurityAnalysis: z.boolean(),
customHeaders: z.string().optional(),
});
type TrackingFormData = z.infer<typeof trackingSchema>;
export function HomePage() {
const [showAdvanced, setShowAdvanced] = useState(false);
const [trackingResult, setTrackingResult] = useState<any>(null);
const { isAuthenticated } = useAuth();
const toast = useToast();
const cardBg = useColorModeValue('white', 'gray.800');
const borderColor = useColorModeValue('gray.200', 'gray.700');
const bgGradient = useColorModeValue(
'linear(to-br, blue.50, purple.50, pink.50)',
'linear(to-br, gray.900, blue.900, purple.900)'
);
const {
register,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<TrackingFormData>({
resolver: zodResolver(trackingSchema),
defaultValues: {
method: 'GET',
maxHops: 10,
timeout: 15000,
enableSSLAnalysis: true,
enableSEOAnalysis: true,
enableSecurityAnalysis: true,
},
});
const maxHops = watch('maxHops');
const timeout = watch('timeout');
// Tracking mutation
const trackingMutation = useMutation({
mutationFn: async (data: TrackRequestV2) => {
// Track URL submission
trackUrlSubmission(data.url, isAuthenticated);
return await trackingApi.trackUrlV2(data);
},
onSuccess: (result) => {
setTrackingResult(result);
toast({
title: 'Tracking completed',
description: `Found ${result.check.redirectCount} redirects`,
status: 'success',
duration: 3000,
isClosable: true,
});
// Track analysis results
const analysis = (result as any).check?.analysis;
if (analysis) {
trackAnalysisResult(
(result as any).check.redirectCount || 0,
analysis.ssl?.warningsJson?.length > 0 || false,
!analysis.seo?.robotsTxtStatus || analysis.seo?.noindex || false,
analysis.security?.mixedContent === 'PRESENT' || analysis.security?.safeBrowsingStatus !== 'safe' || false
);
}
// Don't auto-navigate on home page to allow users to see results
// They can manually navigate to their dashboard to see saved results
},
onError: (error: any) => {
// Track errors
trackError('tracking_failed', error.response?.data?.message || error.message || 'Unknown error');
toast({
title: 'Tracking failed',
description: error.response?.data?.message || 'An error occurred',
status: 'error',
duration: 5000,
isClosable: true,
});
},
});
const onSubmit = (data: TrackingFormData) => {
let headers: Record<string, string> = {};
// Parse custom headers if provided
if (data.customHeaders) {
try {
const lines = data.customHeaders.split('\n').filter(line => line.trim());
for (const line of lines) {
const [key, ...valueParts] = line.split(':');
if (key && valueParts.length > 0) {
headers[key.trim()] = valueParts.join(':').trim();
}
}
} catch (error) {
toast({
title: 'Invalid headers format',
description: 'Please use format: Header-Name: Header-Value',
status: 'error',
duration: 3000,
isClosable: true,
});
return;
}
}
const trackRequest: TrackRequestV2 = {
url: data.url,
method: data.method,
userAgent: data.userAgent,
headers: Object.keys(headers).length > 0 ? headers : undefined,
maxHops: data.maxHops,
timeout: data.timeout,
enableSSLAnalysis: data.enableSSLAnalysis,
enableSEOAnalysis: data.enableSEOAnalysis,
enableSecurityAnalysis: data.enableSecurityAnalysis,
};
trackingMutation.mutate(trackRequest);
};
const features = [
{
icon: FiActivity,
title: 'Real-time Tracking',
description: 'Track redirects in real-time with comprehensive hop analysis and performance metrics.',
color: 'blue',
},
{
icon: FiLock,
title: 'SSL Analysis',
description: 'Comprehensive SSL certificate analysis with expiry warnings and security insights.',
color: 'green',
},
{
icon: FiSearch,
title: 'SEO Optimization',
description: 'Check meta tags, robots.txt, canonical URLs, and other SEO factors.',
color: 'purple',
},
{
icon: FiShield,
title: 'Security Scanning',
description: 'Detect security vulnerabilities, mixed content, and safe browsing status.',
color: 'red',
},
{
icon: FiUpload,
title: 'Bulk Processing',
description: 'Upload CSV files to track thousands of URLs with background processing.',
color: 'orange',
},
{
icon: FiBarChart,
title: 'Advanced Analytics',
description: 'Generate detailed reports with Mermaid diagrams and export to PDF/Markdown.',
color: 'teal',
},
];
const stats = [
{
label: 'Redirect Hops Tracked',
value: '10M+',
helpText: 'Total redirect hops analyzed',
icon: FiTrendingUp,
},
{
label: 'SSL Certificates Analyzed',
value: '2.5M+',
helpText: 'Certificates checked for security',
icon: FiLock,
},
{
label: 'Security Scans',
value: '1M+',
helpText: 'Vulnerability assessments performed',
icon: FiShield,
},
{
label: 'Average Response Time',
value: '<500ms',
helpText: 'Lightning-fast analysis',
icon: FiZap,
},
];
return (
<Box>
{/* Hero Section */}
<Box bgGradient={bgGradient} py={{ base: 16, md: 24 }}>
<Container maxW="6xl">
<VStack spacing={8} textAlign="center">
<Badge colorScheme="brand" variant="subtle" px={4} py={2} borderRadius="full">
🚀 URL Tracker Tool V2 - Now Available
</Badge>
<VStack spacing={4}>
<Heading
as="h1"
size={{ base: "xl", md: "2xl" }}
bgGradient="linear(to-r, brand.400, purple.400, pink.400)"
bgClip="text"
fontWeight="bold"
>
URL Redirect Tracking & Analysis
</Heading>
<Text fontSize={{ base: "lg", md: "xl" }} color="gray.600" maxW="2xl">
Track redirect chains, analyze SSL certificates, optimize SEO, and scan for security vulnerabilities with our comprehensive platform.
</Text>
</VStack>
{/* Advanced Tracking Form */}
<Card bg={cardBg} border="1px solid" borderColor={borderColor} maxW="4xl" w="full">
<CardHeader>
<HStack justify="space-between">
<Heading as="h3" size="md">
URL Redirect Tracker
</Heading>
<Badge colorScheme="brand">Enhanced v2</Badge>
</HStack>
</CardHeader>
<CardBody>
<form onSubmit={handleSubmit(onSubmit)}>
<VStack spacing={6} align="stretch">
{/* URL Input */}
<FormControl isInvalid={!!errors.url}>
<FormLabel>URL to Track</FormLabel>
<Input
{...register('url')}
placeholder="https://example.com or example.com"
size="lg"
/>
{errors.url && (
<Text color="red.500" fontSize="sm" mt={1}>
{errors.url.message}
</Text>
)}
<FormHelperText>
Enter the URL you want to track. Protocol (http/https) is optional.
</FormHelperText>
</FormControl>
{/* Method Selection */}
<FormControl>
<FormLabel>HTTP Method</FormLabel>
<Select {...register('method')}>
<option value="GET">GET</option>
<option value="HEAD">HEAD</option>
<option value="POST">POST</option>
</Select>
<FormHelperText>
HTTP method to use for the initial request
</FormHelperText>
</FormControl>
{/* Analysis Options */}
<FormControl>
<FormLabel>Analysis Options</FormLabel>
<VStack align="start" spacing={3}>
<HStack justify="space-between" w="full">
<Text>SSL Certificate Analysis</Text>
<Switch {...register('enableSSLAnalysis')} colorScheme="brand" />
</HStack>
<HStack justify="space-between" w="full">
<Text>SEO Optimization Analysis</Text>
<Switch {...register('enableSEOAnalysis')} colorScheme="brand" />
</HStack>
<HStack justify="space-between" w="full">
<Text>Security Vulnerability Scan</Text>
<Switch {...register('enableSecurityAnalysis')} colorScheme="brand" />
</HStack>
</VStack>
<FormHelperText>
Enable advanced analysis features (recommended)
</FormHelperText>
</FormControl>
{/* Advanced Options Toggle */}
<Button
variant="ghost"
onClick={() => setShowAdvanced(!showAdvanced)}
size="sm"
>
{showAdvanced ? 'Hide' : 'Show'} Advanced Options
</Button>
{/* Advanced Options */}
<Collapse in={showAdvanced}>
<VStack spacing={6} align="stretch">
<Divider />
{/* Max Hops */}
<FormControl>
<FormLabel>Maximum Hops: {maxHops}</FormLabel>
<Slider
value={maxHops}
onChange={(value) => setValue('maxHops', value)}
min={1}
max={20}
step={1}
colorScheme="brand"
>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
<SliderThumb />
</Slider>
<FormHelperText>
Maximum number of redirects to follow (1-20)
</FormHelperText>
</FormControl>
{/* Timeout */}
<FormControl>
<FormLabel>Timeout (milliseconds)</FormLabel>
<NumberInput
value={timeout}
onChange={(valueString) => setValue('timeout', parseInt(valueString) || 15000)}
min={1000}
max={30000}
step={1000}
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
<FormHelperText>
Request timeout in milliseconds (1000-30000)
</FormHelperText>
</FormControl>
{/* User Agent */}
<FormControl>
<FormLabel>Custom User Agent</FormLabel>
<Input
{...register('userAgent')}
placeholder="Mozilla/5.0 (compatible; RedirectTracker/2.0)"
/>
<FormHelperText>
Custom User-Agent header (optional)
</FormHelperText>
</FormControl>
{/* Custom Headers */}
<FormControl>
<FormLabel>Custom Headers</FormLabel>
<Textarea
{...register('customHeaders')}
placeholder="Accept: application/json&#10;X-Custom-Header: value"
rows={4}
resize="vertical"
/>
<FormHelperText>
Custom headers, one per line in format: Header-Name: Header-Value
</FormHelperText>
</FormControl>
</VStack>
</Collapse>
{/* Rate Limiting Warning */}
{!isAuthenticated && (
<Alert status="info" borderRadius="md">
<AlertIcon />
<VStack align="start" spacing={1}>
<Text fontWeight="medium">Anonymous Usage</Text>
<Text fontSize="sm">
Anonymous users are limited to 50 requests per hour.
<Text as="span" color="brand.500" fontWeight="medium">
{' '}Sign up for higher limits and saved results.
</Text>
</Text>
</VStack>
</Alert>
)}
{/* Submit Button */}
<Button
type="submit"
colorScheme="brand"
size="lg"
isLoading={trackingMutation.isPending}
loadingText="Tracking..."
>
Track URL
</Button>
</VStack>
</form>
</CardBody>
</Card>
</VStack>
</Container>
</Box>
<Container maxW="7xl" py={16}>
<VStack spacing={16}>
{/* Stats Section */}
<Box w="full">
<VStack spacing={8} textAlign="center">
<Heading as="h2" size="xl">
Trusted by Professionals Worldwide
</Heading>
<SimpleGrid columns={{ base: 2, md: 4 }} spacing={8} w="full">
{stats.map((stat, index) => (
<Card key={index} bg={cardBg} border="1px solid" borderColor={borderColor}>
<CardBody textAlign="center">
<Stat>
<VStack spacing={2}>
<Icon as={stat.icon} size="2rem" color="brand.500" />
<StatNumber fontSize="2xl" color="brand.600">
{stat.value}
</StatNumber>
<StatLabel fontSize="sm">{stat.label}</StatLabel>
<StatHelpText fontSize="xs">{stat.helpText}</StatHelpText>
</VStack>
</Stat>
</CardBody>
</Card>
))}
</SimpleGrid>
</VStack>
</Box>
{/* Features Section */}
<Box w="full">
<VStack spacing={12} textAlign="center">
<VStack spacing={4}>
<Heading as="h2" size="xl">
Comprehensive Analysis Platform
</Heading>
<Text fontSize="lg" color="gray.600" maxW="3xl">
Everything you need to understand, optimize, and secure your redirect chains with professional-grade tools and insights.
</Text>
</VStack>
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} spacing={8} w="full">
{features.map((feature, index) => (
<Card key={index} bg={cardBg} border="1px solid" borderColor={borderColor} h="full">
<CardBody>
<VStack spacing={4} align="start" h="full">
<HStack>
<Icon as={feature.icon} size="1.5rem" color={`${feature.color}.500`} />
<Heading as="h3" size="md">
{feature.title}
</Heading>
</HStack>
<Text color="gray.600" flex="1">
{feature.description}
</Text>
<Button
variant="ghost"
size="sm"
rightIcon={<Icon as={FiArrowRight} />}
colorScheme={feature.color}
>
Learn More
</Button>
</VStack>
</CardBody>
</Card>
))}
</SimpleGrid>
</VStack>
</Box>
{/* Use Cases Section */}
<Box w="full">
<VStack spacing={12}>
<VStack spacing={4} textAlign="center">
<Heading as="h2" size="xl">
Perfect for Every Use Case
</Heading>
<Text fontSize="lg" color="gray.600" maxW="3xl">
From individual developers to enterprise teams, our platform scales to meet your redirect tracking needs.
</Text>
</VStack>
<SimpleGrid columns={{ base: 1, lg: 3 }} spacing={8} w="full">
<Card bg={cardBg} border="1px solid" borderColor={borderColor}>
<CardBody>
<VStack spacing={4} align="start">
<Badge colorScheme="blue" variant="subtle">
Developers
</Badge>
<Heading as="h3" size="md">
Debug & Optimize
</Heading>
<Text color="gray.600">
Track redirect chains, identify performance bottlenecks, and ensure proper HTTP status codes for your applications.
</Text>
<VStack align="start" spacing={2} fontSize="sm">
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Real-time redirect analysis</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Performance metrics</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>API integration</Text>
</HStack>
</VStack>
</VStack>
</CardBody>
</Card>
<Card bg={cardBg} border="1px solid" borderColor={borderColor}>
<CardBody>
<VStack spacing={4} align="start">
<Badge colorScheme="green" variant="subtle">
SEO Teams
</Badge>
<Heading as="h3" size="md">
SEO Monitoring
</Heading>
<Text color="gray.600">
Monitor redirect chains for SEO impact, track canonical URLs, and ensure proper meta tag implementation.
</Text>
<VStack align="start" spacing={2} fontSize="sm">
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Meta tag analysis</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Canonical URL tracking</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Bulk URL processing</Text>
</HStack>
</VStack>
</VStack>
</CardBody>
</Card>
<Card bg={cardBg} border="1px solid" borderColor={borderColor}>
<CardBody>
<VStack spacing={4} align="start">
<Badge colorScheme="red" variant="subtle">
Security Teams
</Badge>
<Heading as="h3" size="md">
Security Audits
</Heading>
<Text color="gray.600">
Scan for security vulnerabilities, track SSL certificates, and monitor for malicious redirects.
</Text>
<VStack align="start" spacing={2} fontSize="sm">
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>SSL certificate monitoring</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Security vulnerability scans</Text>
</HStack>
<HStack>
<Icon as={FiCheckCircle} color="green.500" />
<Text>Malicious redirect detection</Text>
</HStack>
</VStack>
</VStack>
</CardBody>
</Card>
</SimpleGrid>
</VStack>
</Box>
{/* CTA Section */}
<Box w="full">
<Card bg="brand.50" border="1px solid" borderColor="brand.200">
<CardBody py={12}>
<VStack spacing={6} textAlign="center">
<Heading as="h2" size="xl" color="brand.700">
Ready to Get Started?
</Heading>
<Text fontSize="lg" color="brand.600" maxW="2xl">
Join thousands of developers, SEO professionals, and security teams who trust our platform for comprehensive redirect analysis.
</Text>
<HStack spacing={4}>
<Button
as={RouterLink}
to={isAuthenticated ? "/dashboard" : "/register"}
colorScheme="brand"
size="lg"
leftIcon={<Icon as={FiZap} />}
>
{isAuthenticated ? "Go to Dashboard" : "Start Free Trial"}
</Button>
<Button
as={RouterLink}
to="/track"
variant="outline"
colorScheme="brand"
size="lg"
>
Try Demo
</Button>
</HStack>
<Text fontSize="sm" color="brand.600">
No credit card required Free tier available Enterprise plans from $99/month
</Text>
</VStack>
</CardBody>
</Card>
</Box>
</VStack>
</Container>
{/* Results */}
{trackingResult && (
<Container maxW="7xl" py={16}>
<TrackingResults result={trackingResult} />
</Container>
)}
</Box>
);
}