Prepare for GitHub Pages deployment

This commit is contained in:
eshanized
2024-12-25 05:00:53 +05:30
parent 2340d0f691
commit 138d4a9d2b
80 changed files with 7421 additions and 0 deletions

28
eslint.config.js Normal file
View File

@@ -0,0 +1,28 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
);

14
index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=Fira+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<title>Snigdha OS - Advanced Penetration Testing Distribution</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

4538
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
package.json Normal file
View File

@@ -0,0 +1,42 @@
{
"name": "snigdhaos-web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
"dependencies": {
"@tanstack/react-query": "^5.25.0",
"clsx": "^2.1.0",
"framer-motion": "^11.0.8",
"lucide-react": "^0.344.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
"react-router-dom": "^6.22.3",
"tailwind-merge": "^2.2.1"
},
"devDependencies": {
"@eslint/js": "^9.9.1",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.18",
"eslint": "^9.9.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.11",
"gh-pages": "^6.2.0",
"globals": "^15.9.0",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"typescript": "^5.5.3",
"typescript-eslint": "^8.3.0",
"vite": "^5.4.2"
}
}

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

35
src/App.tsx Normal file
View File

@@ -0,0 +1,35 @@
import { BrowserRouter as Router } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ErrorBoundary } from '@/components/ui/ErrorBoundary';
import { Navbar } from '@/components/layout/Navbar';
import { Footer } from '@/components/layout/Footer';
import { AppRoutes } from '@/routes';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
retry: 1,
},
},
});
function App() {
return (
<ErrorBoundary>
<QueryClientProvider client={queryClient}>
<Router>
<div className="min-h-screen bg-gray-50 font-fira-sans flex flex-col">
<Navbar />
<main className="flex-grow pt-16">
<AppRoutes />
</main>
<Footer />
</div>
</Router>
</QueryClientProvider>
</ErrorBoundary>
);
}
export default App;

View File

@@ -0,0 +1,34 @@
import { motion } from 'framer-motion';
import { Target } from 'lucide-react';
export function MissionSection() {
return (
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
className="bg-white/80 backdrop-blur-sm p-8 rounded-lg shadow-sm"
>
<div className="flex items-center gap-3 mb-6">
<Target className="h-6 w-6 text-cornflower-blue" />
<h2 className="text-2xl font-bold text-gray-900">Our Mission</h2>
</div>
<div className="prose prose-gray max-w-none">
<p className="text-gray-600 leading-relaxed">
Snigdha OS aims to provide security professionals and enthusiasts with the most comprehensive,
reliable, and up-to-date collection of security tools. Our mission is to enable the security
community to perform professional-grade security auditing and penetration testing with a
standardized, well-documented platform.
</p>
<h3 className="text-xl font-semibold mt-6 mb-3">Core Values</h3>
<ul className="space-y-2 text-gray-600">
<li>Open Source: Maintaining transparency and community collaboration</li>
<li>Security: Providing robust tools for security testing</li>
<li>Education: Supporting learning and skill development</li>
<li>Community: Fostering a strong, supportive user community</li>
</ul>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,43 @@
import { motion } from 'framer-motion';
import { Users } from 'lucide-react';
const teamStructure = [
{
title: 'Core Development',
description: 'Responsible for the base system and core tools integration',
},
{
title: 'Security Tools',
description: 'Maintains and updates the vast collection of security tools',
},
{
title: 'Documentation',
description: 'Creates and maintains user documentation and guides',
},
{
title: 'Community Management',
description: 'Manages community interactions and contributions',
},
];
export function TeamSection() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{teamStructure.map((team, index) => (
<motion.div
key={team.title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg shadow-sm"
>
<div className="flex items-center gap-3 mb-3">
<Users className="h-5 w-5 text-cornflower-blue" />
<h3 className="text-lg font-semibold text-gray-900">{team.title}</h3>
</div>
<p className="text-gray-600">{team.description}</p>
</motion.div>
))}
</div>
);
}

View File

@@ -0,0 +1,40 @@
import { motion } from 'framer-motion';
import { Calendar } from 'lucide-react';
const releases = [
{ version: '2024.1', date: '2024', description: 'Latest release with enhanced cloud support' },
{ version: '2023.4', date: '2023', description: 'Major UI overhaul and tool updates' },
{ version: '2023.1', date: '2023', description: 'Introduced new wireless testing tools' },
{ version: '2022.4', date: '2022', description: 'Added ARM64 support improvements' },
{ version: '2022.1', date: '2022', description: 'Enhanced container support' },
];
export function Timeline() {
return (
<div className="relative">
<div className="absolute left-4 top-0 bottom-0 w-0.5 bg-gray-200" />
{releases.map((release, index) => (
<motion.div
key={release.version}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className="relative pl-12 pb-8"
>
<div className="absolute left-0 p-2 bg-white rounded-full border-2 border-cornflower-blue">
<Calendar className="h-4 w-4 text-cornflower-blue" />
</div>
<div className="bg-white/80 backdrop-blur-sm p-4 rounded-lg shadow-sm">
<h3 className="text-lg font-semibold text-gray-900">
Kali {release.version}
</h3>
<time className="text-sm text-gray-500">{release.date}</time>
<p className="mt-1 text-gray-600">{release.description}</p>
</div>
</motion.div>
))}
</div>
);
}

View File

@@ -0,0 +1,43 @@
import { motion } from 'framer-motion';
import { Github, GitCommit } from 'lucide-react';
interface ContributorCardProps {
login: string;
avatarUrl: string;
contributions: number;
profileUrl: string;
}
export function ContributorCard({ login, avatarUrl, contributions, profileUrl }: ContributorCardProps) {
return (
<motion.div
whileHover={{ y: -5 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg border border-gray-200"
>
<div className="flex items-center gap-4">
<img
src={avatarUrl}
alt={`${login}'s avatar`}
className="w-16 h-16 rounded-full"
/>
<div>
<h3 className="font-semibold text-gray-900">{login}</h3>
<div className="flex items-center gap-2 text-sm text-gray-600">
<GitCommit className="h-4 w-4" />
<span>{contributions} contributions</span>
</div>
</div>
</div>
<a
href={profileUrl}
target="_blank"
rel="noopener noreferrer"
className="mt-4 flex items-center gap-2 text-sm text-cornflower-blue hover:underline"
>
<Github className="h-4 w-4" />
View Profile
</a>
</motion.div>
);
}

View File

@@ -0,0 +1,25 @@
import { Users, GitPullRequest, GitCommit } from 'lucide-react';
interface Stats {
totalContributors: number;
totalPullRequests: number;
totalCommits: number;
}
export function ContributorStats({ totalContributors, totalPullRequests, totalCommits }: Stats) {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[
{ icon: Users, label: 'Contributors', value: totalContributors },
{ icon: GitPullRequest, label: 'Pull Requests', value: totalPullRequests },
{ icon: GitCommit, label: 'Commits', value: totalCommits },
].map(({ icon: Icon, label, value }) => (
<div key={label} className="bg-white/80 backdrop-blur-sm p-6 rounded-lg">
<Icon className="h-8 w-8 text-cornflower-blue mb-2" />
<p className="text-2xl font-bold text-gray-900">{value.toLocaleString()}</p>
<p className="text-sm text-gray-600">{label}</p>
</div>
))}
</div>
);
}

View File

@@ -0,0 +1,56 @@
import { motion } from 'framer-motion';
import { Star, GitFork, Clock } from 'lucide-react';
import { formatDate } from '@/lib/utils';
import type { GithubRepo } from '@/lib/github';
interface RepoCardProps {
repo: GithubRepo;
}
export function RepoCard({ repo }: RepoCardProps) {
return (
<motion.div
whileHover={{ y: -5 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg border border-gray-200"
>
<h3 className="text-lg font-semibold text-gray-900">
<a
href={repo.html_url}
target="_blank"
rel="noopener noreferrer"
className="hover:text-cornflower-blue transition-colors"
>
{repo.name}
</a>
</h3>
{repo.description && (
<p className="mt-2 text-gray-600 line-clamp-2">{repo.description}</p>
)}
<div className="mt-4 flex items-center gap-4 text-sm text-gray-500">
{repo.language && (
<span className="flex items-center gap-1">
<span className="w-3 h-3 rounded-full bg-cornflower-blue" />
{repo.language}
</span>
)}
<span className="flex items-center gap-1">
<Star className="h-4 w-4" />
{repo.stargazers_count}
</span>
<span className="flex items-center gap-1">
<GitFork className="h-4 w-4" />
{repo.forks_count}
</span>
<span className="flex items-center gap-1">
<Clock className="h-4 w-4" />
{formatDate(repo.updated_at)}
</span>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,58 @@
import { motion } from 'framer-motion';
import { MapPin, Users, Book } from 'lucide-react';
import type { GithubUser } from '@/lib/github';
interface TeamMemberCardProps {
user: GithubUser;
role: string;
description: string;
}
export function TeamMemberCard({ user, role, description }: TeamMemberCardProps) {
return (
<motion.div
whileHover={{ y: -5 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg border border-gray-200"
>
<div className="flex items-start gap-4">
<img
src={user.avatar_url}
alt={`${user.login}'s avatar`}
className="w-16 h-16 rounded-full"
/>
<div>
<h3 className="font-semibold text-gray-900">{user.name || user.login}</h3>
<p className="text-sm text-cornflower-blue">{role}</p>
{user.location && (
<div className="flex items-center gap-1 text-sm text-gray-500 mt-1">
<MapPin className="h-4 w-4" />
<span>{user.location}</span>
</div>
)}
</div>
</div>
<p className="mt-4 text-gray-600">{description}</p>
<div className="mt-4 flex items-center gap-4 text-sm text-gray-500">
<div className="flex items-center gap-1">
<Users className="h-4 w-4" />
<span>{user.followers.toLocaleString()} followers</span>
</div>
<div className="flex items-center gap-1">
<Book className="h-4 w-4" />
<span>{user.public_repos} repos</span>
</div>
</div>
<a
href={user.html_url}
target="_blank"
rel="noopener noreferrer"
className="mt-4 inline-flex items-center text-sm text-cornflower-blue hover:underline"
>
View GitHub Profile
</a>
</motion.div>
);
}

View File

@@ -0,0 +1,46 @@
import { motion } from 'framer-motion';
import { Check } from 'lucide-react';
interface DonationTierProps {
name: string;
amount: number;
benefits: string[];
recommended?: boolean;
onSelect: () => void;
}
export function DonationTier({ name, amount, benefits, recommended, onSelect }: DonationTierProps) {
return (
<motion.div
whileHover={{ y: -5 }}
className={`bg-white/80 backdrop-blur-sm p-6 rounded-lg border ${
recommended ? 'border-cornflower-blue' : 'border-gray-200'
}`}
>
{recommended && (
<span className="inline-block px-3 py-1 text-xs font-medium text-cornflower-blue bg-blue-50 rounded-full mb-4">
Recommended
</span>
)}
<h3 className="text-xl font-semibold text-gray-900">{name}</h3>
<p className="mt-2 text-3xl font-bold text-gray-900">${amount}</p>
<ul className="mt-6 space-y-3">
{benefits.map((benefit) => (
<li key={benefit} className="flex items-start gap-2">
<Check className="h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" />
<span className="text-gray-600">{benefit}</span>
</li>
))}
</ul>
<button
onClick={onSelect}
className="mt-8 w-full py-2 px-4 bg-cornflower-blue text-white rounded-lg hover:bg-blue-600 transition-colors"
>
Select
</button>
</motion.div>
);
}

View File

@@ -0,0 +1,45 @@
import { motion } from 'framer-motion';
import { Heart } from 'lucide-react';
interface Donor {
name: string;
amount: number;
message?: string;
date: string;
}
interface DonorWallProps {
donors: Donor[];
}
export function DonorWall({ donors }: DonorWallProps) {
return (
<div className="bg-white/80 backdrop-blur-sm p-6 rounded-lg">
<h2 className="text-2xl font-semibold text-gray-900 mb-6">Recent Supporters</h2>
<div className="space-y-6">
{donors.map((donor, index) => (
<motion.div
key={`${donor.name}-${donor.date}`}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="flex items-start gap-4"
>
<Heart className="h-5 w-5 text-red-500 flex-shrink-0 mt-1" />
<div>
<div className="flex items-center gap-2">
<h3 className="font-medium text-gray-900">{donor.name}</h3>
<span className="text-sm text-cornflower-blue">${donor.amount}</span>
</div>
{donor.message && (
<p className="mt-1 text-sm text-gray-600">{donor.message}</p>
)}
<time className="text-xs text-gray-500">{donor.date}</time>
</div>
</motion.div>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,18 @@
import { Github } from 'lucide-react';
import { motion } from 'framer-motion';
export function GithubSponsorButton() {
return (
<motion.a
href="https://github.com/sponsors/eshanized"
target="_blank"
rel="noopener noreferrer"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="inline-flex items-center gap-2 px-6 py-3 bg-[#2ea44f] text-white rounded-lg hover:bg-[#2c974b] transition-colors"
>
<Github className="h-5 w-5" />
Sponsor on GitHub
</motion.a>
);
}

View File

@@ -0,0 +1,46 @@
import { motion } from 'framer-motion';
import { Building2 } from 'lucide-react';
const keySponsors = [
{
name: 'TONMOY INFRASTRUCTURE',
description: 'Enterprise Partner & Infrastructure Provider',
logo: Building2,
},
{
name: 'IX INTERNATION CO.',
description: 'Strategic Development Partner',
logo: Building2,
},
];
export function KeySponsors() {
return (
<div className="bg-gradient-to-r from-cornflower-blue/5 to-blue-50/50 rounded-2xl p-8">
<h2 className="text-2xl font-bold text-gray-900 text-center mb-8">
Key Sponsors
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{keySponsors.map((sponsor, index) => (
<motion.div
key={sponsor.name}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="bg-white/90 backdrop-blur-sm rounded-xl p-6 border border-cornflower-blue/20 shadow-sm"
>
<div className="flex items-center gap-4">
<div className="p-3 bg-cornflower-blue/10 rounded-lg">
<sponsor.logo className="h-8 w-8 text-cornflower-blue" />
</div>
<div>
<h3 className="font-semibold text-gray-900">{sponsor.name}</h3>
<p className="text-sm text-gray-600">{sponsor.description}</p>
</div>
</div>
</motion.div>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,44 @@
import { motion } from 'framer-motion';
import { Users, Star, Heart } from 'lucide-react';
import { formatINR } from '@/lib/currency';
const stats = [
{
label: 'Current Sponsors',
value: '50+',
icon: Users,
color: 'text-blue-500'
},
{
label: 'Monthly Support',
value: formatINR(200000),
icon: Heart,
color: 'text-red-500'
},
{
label: 'GitHub Stars',
value: '1.2K+',
icon: Star,
color: 'text-yellow-500'
}
];
export function SponsorshipStats() {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{stats.map((stat, index) => (
<motion.div
key={stat.label}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-xl border border-gray-200"
>
<stat.icon className={`h-8 w-8 ${stat.color} mb-2`} />
<p className="text-2xl font-bold text-gray-900">{stat.value}</p>
<p className="text-gray-600">{stat.label}</p>
</motion.div>
))}
</div>
);
}

View File

@@ -0,0 +1,98 @@
import { motion } from 'framer-motion';
import { Check } from 'lucide-react';
import { formatINR } from '@/lib/currency';
const tiers = [
{
name: 'Community Hero',
amount: 399,
description: 'Support the ongoing development of Snigdha OS',
benefits: [
'Special recognition on our GitHub repository',
'Access to sponsor-only updates',
'Vote on feature priorities'
]
},
{
name: 'Security Champion',
amount: 799,
description: 'Help shape the future of security testing',
benefits: [
'All Community Hero benefits',
'Early access to new features',
'Priority support on GitHub',
'Exclusive security tips newsletter'
],
featured: true
},
{
name: 'Enterprise Partner',
amount: 1999,
description: 'Perfect for organizations using Snigdha OS',
benefits: [
'All Security Champion benefits',
'Custom support channel',
'Training materials access',
'Team collaboration features',
'Priority feature requests'
]
}
];
export function SponsorshipTiers() {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{tiers.map((tier) => (
<motion.div
key={tier.name}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
className={`relative rounded-2xl ${
tier.featured
? 'bg-gradient-to-b from-cornflower-blue/10 to-cornflower-blue/5 border-2 border-cornflower-blue'
: 'bg-white/80 border border-gray-200'
} backdrop-blur-sm p-6 shadow-lg`}
>
{tier.featured && (
<div className="absolute -top-4 left-1/2 -translate-x-1/2">
<span className="px-4 py-1 bg-cornflower-blue text-white text-sm rounded-full">
Most Popular
</span>
</div>
)}
<div className="mb-4">
<h3 className="text-xl font-semibold text-gray-900">{tier.name}</h3>
<div className="mt-2">
<span className="text-3xl font-bold text-gray-900">{formatINR(tier.amount)}</span>
<span className="text-gray-600">/month</span>
</div>
<p className="mt-2 text-gray-600">{tier.description}</p>
</div>
<ul className="space-y-3 mb-6">
{tier.benefits.map((benefit) => (
<li key={benefit} className="flex items-start gap-2">
<Check className="h-5 w-5 text-green-500 flex-shrink-0 mt-0.5" />
<span className="text-gray-600">{benefit}</span>
</li>
))}
</ul>
<a
href={`https://github.com/sponsors/eshanized?frequency=monthly&sponsor=${encodeURIComponent(tier.name)}`}
target="_blank"
rel="noopener noreferrer"
className={`block w-full text-center py-2 px-4 rounded-lg transition-colors ${
tier.featured
? 'bg-cornflower-blue text-white hover:bg-blue-600'
: 'bg-gray-100 text-gray-900 hover:bg-gray-200'
}`}
>
Become a {tier.name}
</a>
</motion.div>
))}
</div>
);
}

View File

@@ -0,0 +1,29 @@
import { Shield } from 'lucide-react';
interface ChecksumProps {
sha256: string;
gpg: string;
}
export function Checksum({ sha256, gpg }: ChecksumProps) {
return (
<div className="bg-white/80 backdrop-blur-sm p-6 rounded-lg">
<div className="flex items-center gap-2 mb-4">
<Shield className="h-5 w-5 text-cornflower-blue" />
<h2 className="text-xl font-semibold text-gray-900">Verify Download</h2>
</div>
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium text-gray-700 mb-1">SHA256 Checksum</h3>
<code className="block p-2 bg-gray-100 rounded text-sm break-all">{sha256}</code>
</div>
<div>
<h3 className="text-sm font-medium text-gray-700 mb-1">GPG Signature</h3>
<code className="block p-2 bg-gray-100 rounded text-sm break-all">{gpg}</code>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,25 @@
import { Download } from 'lucide-react';
import { motion } from 'framer-motion';
interface DownloadButtonProps {
version: string;
size: string;
url: string;
}
export function DownloadButton({ version, size, url }: DownloadButtonProps) {
return (
<motion.a
href={url}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
className="flex items-center justify-between w-full p-4 bg-white/80 backdrop-blur-sm rounded-lg border border-gray-200 hover:border-cornflower-blue transition-colors"
>
<div>
<h3 className="font-semibold text-gray-900">Snigdha OS {version}</h3>
<p className="text-sm text-gray-500">{size}</p>
</div>
<Download className="h-5 w-5 text-cornflower-blue" />
</motion.a>
);
}

View File

@@ -0,0 +1,48 @@
import { motion } from 'framer-motion';
import { Globe, Wifi, Download } from 'lucide-react';
import { type Mirror } from '@/types/download';
import { formatSpeed } from '@/lib/utils';
interface MirrorListProps {
mirrors: Mirror[];
onSelect: (mirror: Mirror) => void;
}
export function MirrorList({ mirrors, onSelect }: MirrorListProps) {
return (
<div className="space-y-4">
{mirrors.map((mirror, index) => (
<motion.div
key={mirror.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="bg-white/80 backdrop-blur-sm p-4 rounded-lg border border-gray-200 hover:border-cornflower-blue transition-colors"
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Globe className="h-5 w-5 text-cornflower-blue" />
<div>
<h3 className="font-medium text-gray-900">{mirror.name}</h3>
<p className="text-sm text-gray-500">{mirror.location}</p>
</div>
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-1 text-sm text-gray-600">
<Wifi className="h-4 w-4" />
{formatSpeed(mirror.speed)}
</div>
<button
onClick={() => onSelect(mirror)}
className="flex items-center gap-2 px-4 py-2 bg-cornflower-blue text-white rounded-lg hover:bg-blue-600 transition-colors"
>
<Download className="h-4 w-4" />
Select
</button>
</div>
</div>
</motion.div>
))}
</div>
);
}

View File

@@ -0,0 +1,39 @@
import { Wifi } from 'lucide-react';
interface NetworkSpeedProps {
speed: number;
}
export function NetworkSpeed({ speed }: NetworkSpeedProps) {
const getRecommendation = (speed: number) => {
if (speed >= 100) return 'Excellent for fast downloads';
if (speed >= 50) return 'Good for normal downloads';
if (speed >= 20) return 'Moderate speed, downloads may take longer';
return 'Slow connection, consider using a different mirror';
};
const getSpeedClass = (speed: number) => {
if (speed >= 100) return 'text-green-500';
if (speed >= 50) return 'text-blue-500';
if (speed >= 20) return 'text-yellow-500';
return 'text-red-500';
};
return (
<div className="bg-white/80 backdrop-blur-sm p-6 rounded-lg">
<div className="flex items-center gap-2 mb-4">
<Wifi className="h-5 w-5 text-cornflower-blue" />
<h2 className="text-xl font-semibold text-gray-900">Network Speed</h2>
</div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<span className={`text-2xl font-bold ${getSpeedClass(speed)}`}>
{speed} Mbps
</span>
</div>
<p className="text-gray-600">{getRecommendation(speed)}</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,34 @@
import { MapPin } from 'lucide-react';
import { type Mirror } from '@/types/download';
import { type UserLocation } from '@/lib/location';
interface SuggestedMirrorProps {
mirror: Mirror;
userLocation: UserLocation;
onSelect: (mirror: Mirror) => void;
}
export function SuggestedMirror({ mirror, userLocation, onSelect }: SuggestedMirrorProps) {
return (
<div className="bg-white/80 backdrop-blur-sm p-6 rounded-lg border-2 border-cornflower-blue">
<div className="flex items-center gap-2 mb-4">
<MapPin className="h-5 w-5 text-cornflower-blue" />
<div>
<h3 className="font-medium text-gray-900">Suggested Mirror</h3>
<p className="text-sm text-gray-500">Based on your location: {userLocation.city}, {userLocation.country}</p>
</div>
</div>
<div className="space-y-2">
<p className="font-medium text-gray-900">{mirror.name}</p>
<p className="text-sm text-gray-600">{mirror.location}</p>
<button
onClick={() => onSelect(mirror)}
className="w-full mt-2 px-4 py-2 bg-cornflower-blue text-white rounded-lg hover:bg-blue-600 transition-colors"
>
Use This Mirror
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,25 @@
import { Check } from 'lucide-react';
const requirements = [
'Minimum 2GB RAM (4GB recommended)',
'20GB disk space',
'CPU with virtualization support',
'DVD drive / USB boot support',
'Internet connectivity for updates',
];
export function SystemRequirements() {
return (
<div className="bg-white/80 backdrop-blur-sm p-6 rounded-lg">
<h2 className="text-xl font-semibold text-gray-900 mb-4">System Requirements</h2>
<ul className="space-y-3">
{requirements.map((req) => (
<li key={req} className="flex items-center gap-2">
<Check className="h-5 w-5 text-green-500 flex-shrink-0" />
<span className="text-gray-600">{req}</span>
</li>
))}
</ul>
</div>
);
}

View File

@@ -0,0 +1,29 @@
import { motion } from 'framer-motion';
interface CategoryFilterProps {
categories: string[];
selectedCategory: string;
onSelect: (category: string) => void;
}
export function CategoryFilter({ categories, selectedCategory, onSelect }: CategoryFilterProps) {
return (
<div className="flex flex-wrap gap-2">
{categories.map((category) => (
<motion.button
key={category}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => onSelect(category)}
className={`px-4 py-2 rounded-full text-sm font-medium transition-colors ${
selectedCategory === category
? 'bg-cornflower-blue text-white'
: 'bg-white/80 text-gray-600 hover:bg-gray-100'
}`}
>
{category}
</motion.button>
))}
</div>
);
}

View File

@@ -0,0 +1,22 @@
import { Search } from 'lucide-react';
import { ChangeEvent } from 'react';
interface SearchBarProps {
value: string;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
}
export function SearchBar({ value, onChange }: SearchBarProps) {
return (
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
<input
type="text"
value={value}
onChange={onChange}
placeholder="Search tools..."
className="w-full pl-10 pr-4 py-2 bg-white/80 backdrop-blur-sm border border-gray-200 rounded-lg focus:ring-2 focus:ring-cornflower-blue focus:border-transparent outline-none"
/>
</div>
);
}

View File

@@ -0,0 +1,32 @@
import { motion } from 'framer-motion';
import { Terminal } from 'lucide-react';
interface ToolCardProps {
name: string;
description: string;
category: string;
command: string;
}
export function ToolCard({ name, description, category, command }: ToolCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
whileHover={{ y: -5 }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg shadow-sm border border-gray-100"
>
<div className="flex items-center gap-3 mb-3">
<Terminal className="h-5 w-5 text-cornflower-blue" />
<h3 className="text-lg font-semibold text-gray-900">{name}</h3>
</div>
<p className="text-gray-600 mb-4">{description}</p>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-cornflower-blue">{category}</span>
<code className="text-sm bg-gray-100 px-2 py-1 rounded">{command}</code>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,29 @@
import { motion } from 'framer-motion';
interface CategoryFilterProps {
categories: string[];
selectedCategory: string;
onSelect: (category: string) => void;
}
export function CategoryFilter({ categories, selectedCategory, onSelect }: CategoryFilterProps) {
return (
<div className="flex flex-wrap justify-center gap-3 mb-8">
{categories.map((category) => (
<motion.button
key={category}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => onSelect(category)}
className={`px-4 py-2 rounded-full text-sm font-medium transition-colors ${
selectedCategory === category
? 'bg-cornflower-blue text-white'
: 'bg-white text-gray-600 hover:bg-gray-100'
}`}
>
{category}
</motion.button>
))}
</div>
);
}

View File

@@ -0,0 +1,30 @@
import { motion } from 'framer-motion';
interface GalleryImageProps {
src: string;
alt: string;
category: string;
}
export function GalleryImage({ src, alt, category }: GalleryImageProps) {
return (
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
whileHover={{ y: -5 }}
className="relative group overflow-hidden rounded-lg"
>
<img
src={src}
alt={alt}
className="w-full h-64 object-cover transform group-hover:scale-110 transition-transform duration-500"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity">
<div className="absolute bottom-0 left-0 right-0 p-4">
<p className="text-white font-medium">{alt}</p>
<span className="text-sm text-gray-300">{category}</span>
</div>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,60 @@
import { motion } from 'framer-motion';
import { Check, X } from 'lucide-react';
const features = [
'Advanced Security Tools',
'Regular Updates',
'Community Support',
'Hardware Compatibility',
'Custom Tools',
'Enterprise Support',
];
export function ComparisonSection() {
return (
<section className="py-20 bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-gray-900">Why Choose Snigdha OS?</h2>
<p className="mt-4 text-lg text-gray-600">
Compare and see why security professionals prefer Snigdha OS
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{['Other Distros', 'Snigdha OS', 'Commercial Tools'].map((title, colIndex) => (
<motion.div
key={title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: colIndex * 0.2 }}
className={`bg-white rounded-lg shadow-lg overflow-hidden ${
colIndex === 1 ? 'ring-2 ring-cornflower-blue' : ''
}`}
>
<div className={`p-6 ${
colIndex === 1 ? 'bg-cornflower-blue text-white' : 'bg-gray-50'
}`}>
<h3 className="text-xl font-semibold">{title}</h3>
</div>
<div className="p-6">
<ul className="space-y-4">
{features.map((feature) => (
<li key={feature} className="flex items-center gap-2">
{colIndex === 1 ? (
<Check className="h-5 w-5 text-green-500" />
) : (
<X className="h-5 w-5 text-red-500" />
)}
<span className="text-gray-600">{feature}</span>
</li>
))}
</ul>
</div>
</motion.div>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,30 @@
import { motion } from 'framer-motion';
import { LucideIcon } from 'lucide-react';
interface FeatureCardProps {
title: string;
description: string;
icon: LucideIcon;
delay?: number;
}
export function FeatureCard({ title, description, icon: Icon, delay = 0 }: FeatureCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay }}
viewport={{ once: true }}
whileHover={{ y: -5 }}
className="relative group"
>
<div className="rounded-xl bg-white/80 backdrop-blur p-8 ring-1 ring-gray-200 hover:ring-cornflower-blue transition-all shadow-lg overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-cornflower-blue/0 to-cornflower-blue/0 group-hover:from-cornflower-blue/5 group-hover:to-cornflower-blue/10 transition-colors" />
<Icon className="h-10 w-10 text-cornflower-blue mb-4" />
<h3 className="text-xl font-semibold text-gray-900 mb-2">{title}</h3>
<p className="text-gray-600">{description}</p>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,76 @@
import { motion } from 'framer-motion';
import { Link } from 'react-router-dom';
import { Download, ChevronRight, Terminal } from 'lucide-react';
export function HeroSection() {
return (
<section className="relative min-h-[90vh] flex items-center overflow-hidden bg-gradient-to-r from-gray-900 to-gray-800">
<div className="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1629654297299-c8506221ca97?auto=format&fit=crop&q=80')] bg-cover bg-center opacity-10" />
<div className="absolute inset-0 bg-gradient-to-b from-transparent to-gray-900/50" />
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="text-center"
>
<motion.div
animate={{
rotate: [0, 5, -5, 0],
scale: [1, 1.1, 1]
}}
transition={{ duration: 2, repeat: Infinity, repeatDelay: 3 }}
>
<Terminal className="mx-auto h-20 w-20 text-cornflower-blue" />
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 }}
className="mt-6 text-4xl font-bold tracking-tight text-white sm:text-6xl"
>
The Future of
<span className="text-cornflower-blue"> Security Testing </span>
is Here
</motion.h1>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
className="mt-6 text-lg leading-8 text-gray-300 max-w-2xl mx-auto"
>
Snigdha OS redefines penetration testing with advanced tools, intuitive interface, and unmatched performance.
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.9 }}
className="mt-10 flex items-center justify-center gap-x-6"
>
<Link
to="/download"
className="group relative rounded-lg bg-cornflower-blue px-8 py-3 text-sm font-semibold text-white shadow-lg hover:bg-blue-600 transition-colors overflow-hidden"
>
<span className="relative flex items-center gap-2">
<Download className="h-4 w-4" />
Download Now
</span>
<div className="absolute inset-0 bg-white/20 transform -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-700" />
</Link>
<Link
to="/features"
className="text-sm font-semibold leading-6 text-white flex items-center group"
>
Learn more
<ChevronRight className="ml-1 h-4 w-4 transform group-hover:translate-x-1 transition-transform" />
</Link>
</motion.div>
</motion.div>
</div>
</section>
);
}

View File

@@ -0,0 +1,41 @@
import { motion } from 'framer-motion';
import { Users, Wrench, Star } from 'lucide-react';
const stats = [
{ label: 'Active Users', value: '50K+', icon: Users },
{ label: 'Security Tools', value: '600+', icon: Wrench },
{ label: 'GitHub Stars', value: '15K+', icon: Star },
];
export function StatsSection() {
return (
<section className="py-20 bg-gradient-to-b from-white to-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{stats.map((stat, index) => (
<motion.div
key={stat.label}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.2 }}
viewport={{ once: true }}
className="text-center"
>
<stat.icon className="h-8 w-8 text-cornflower-blue mx-auto mb-4" />
<motion.p
initial={{ scale: 0.5 }}
whileInView={{ scale: 1 }}
transition={{ delay: index * 0.2 + 0.2 }}
viewport={{ once: true }}
className="text-4xl font-bold text-gray-900 mb-2"
>
{stat.value}
</motion.p>
<p className="text-gray-600">{stat.label}</p>
</motion.div>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,27 @@
import { motion } from 'framer-motion';
import { Quote } from 'lucide-react';
interface TestimonialProps {
content: string;
author: string;
role: string;
delay?: number;
}
export function TestimonialCard({ content, author, role, delay = 0 }: TestimonialProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay }}
className="bg-white/80 backdrop-blur-sm p-6 rounded-lg shadow-lg relative"
>
<Quote className="absolute top-4 right-4 h-8 w-8 text-cornflower-blue/20" />
<p className="text-gray-600 mb-4">{content}</p>
<div>
<p className="font-semibold text-gray-900">{author}</p>
<p className="text-sm text-gray-500">{role}</p>
</div>
</motion.div>
);
}

View File

@@ -0,0 +1,41 @@
import { motion } from 'framer-motion';
import { Terminal, Shield, Wifi, Globe, Lock, Database } from 'lucide-react';
const tools = [
{ name: 'Network Analysis', icon: Globe, color: 'text-blue-500' },
{ name: 'Penetration Testing', icon: Shield, color: 'text-green-500' },
{ name: 'Wireless Security', icon: Wifi, color: 'text-purple-500' },
{ name: 'Cryptography', icon: Lock, color: 'text-red-500' },
{ name: 'Forensics', icon: Database, color: 'text-yellow-500' },
{ name: 'Exploitation', icon: Terminal, color: 'text-pink-500' },
];
export function ToolsShowcase() {
return (
<section className="py-20 bg-gradient-to-b from-gray-50 to-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-gray-900">Security Tools Suite</h2>
<p className="mt-4 text-lg text-gray-600">
Comprehensive toolkit for security professionals
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-8">
{tools.map((tool, index) => (
<motion.div
key={tool.name}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
className="flex flex-col items-center p-6 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow"
>
<tool.icon className={`h-10 w-10 ${tool.color} mb-4`} />
<h3 className="text-lg font-semibold text-gray-900">{tool.name}</h3>
</motion.div>
))}
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,103 @@
import { Link } from 'react-router-dom';
import { Github, Twitter, Youtube, Mail, Book, FileText, MessageSquare, Newspaper, HelpCircle } from 'lucide-react';
const footerNavigation = {
main: [
{ name: 'Gallery', href: '/gallery' },
{ name: 'Developers', href: '/developers' },
{ name: 'Donate', href: '/donate' },
],
resources: [
{ name: 'Documentation', href: '/docs', icon: Book },
{ name: 'Blog', href: '/blog', icon: Newspaper },
{ name: 'Support', href: '/support', icon: HelpCircle },
],
community: [
{ name: 'Community', href: '/community', icon: MessageSquare },
{ name: 'GitHub', href: 'https://github.com/Snigdha-OS', icon: Github },
{ name: 'Twitter', href: 'https://twitter.com', icon: Twitter },
{ name: 'YouTube', href: 'https://youtube.com', icon: Youtube },
],
};
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-gray-900 text-gray-300">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8">
<div className="space-y-4">
<h3 className="text-lg font-semibold text-white">Snigdha OS</h3>
<p className="text-sm">
The most advanced penetration testing distribution, designed for security professionals and enthusiasts.
</p>
<div className="flex space-x-4">
{footerNavigation.community.map((item) => (
<a
key={item.name}
href={item.href}
className="hover:text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
<item.icon className="h-5 w-5" />
</a>
))}
</div>
</div>
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Navigation</h3>
<ul className="space-y-3">
{footerNavigation.main.map((item) => (
<li key={item.name}>
<Link to={item.href} className="hover:text-white transition-colors">
{item.name}
</Link>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Resources</h3>
<ul className="space-y-3">
{footerNavigation.resources.map((item) => (
<li key={item.name}>
<Link to={item.href} className="flex items-center gap-2 hover:text-white transition-colors">
<item.icon className="h-4 w-4" />
{item.name}
</Link>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Community</h3>
<ul className="space-y-3">
{footerNavigation.community.map((item) => (
<li key={item.name}>
<a
href={item.href}
className="flex items-center gap-2 hover:text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
<item.icon className="h-4 w-4" />
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
<div className="mt-12 pt-8 border-t border-gray-800 text-sm text-center">
<p>&copy; {currentYear} Snigdha OS. All rights reserved.</p>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,28 @@
import { Mail, MapPin, Phone } from 'lucide-react';
export function ContactSection() {
return (
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Contact Us</h3>
<ul className="space-y-3">
<li>
<a
href="mailto:contact@snigdhaos.org"
className="flex items-center gap-2 hover:text-white transition-colors"
>
<Mail className="h-4 w-4" />
contact@snigdhaos.org
</a>
</li>
<li className="flex items-center gap-2">
<MapPin className="h-4 w-4" />
<span>Bangalore, India</span>
</li>
<li className="flex items-center gap-2">
<Phone className="h-4 w-4" />
<span>+91 (080) 4567-8900</span>
</li>
</ul>
</div>
);
}

View File

@@ -0,0 +1,75 @@
import { Link } from 'react-router-dom';
import { Github, Twitter, Youtube, Book, MessageSquare, Newspaper, HelpCircle } from 'lucide-react';
import { ContactSection } from './ContactSection';
import { footerNavigation } from '@/data/footerNavigation';
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-gray-900 text-gray-300">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="grid grid-cols-1 md:grid-cols-5 gap-8">
<div className="space-y-4 md:col-span-2">
<h3 className="text-lg font-semibold text-white">Snigdha OS</h3>
<p className="text-sm">
The most advanced penetration testing distribution, designed for security professionals and enthusiasts.
</p>
<div className="flex space-x-4">
{footerNavigation.community.map((item) => (
<a
key={item.name}
href={item.href}
className="hover:text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
<item.icon className="h-5 w-5" />
</a>
))}
</div>
</div>
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Resources</h3>
<ul className="space-y-3">
{footerNavigation.resources.map((item) => (
<li key={item.name}>
<Link to={item.href} className="flex items-center gap-2 hover:text-white transition-colors">
<item.icon className="h-4 w-4" />
{item.name}
</Link>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-semibold text-white uppercase tracking-wider mb-4">Community</h3>
<ul className="space-y-3">
{footerNavigation.community.map((item) => (
<li key={item.name}>
<a
href={item.href}
className="flex items-center gap-2 hover:text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
<item.icon className="h-4 w-4" />
{item.name}
</a>
</li>
))}
</ul>
</div>
<ContactSection />
</div>
<div className="mt-12 pt-8 border-t border-gray-800 text-sm text-center">
<p>&copy; {currentYear} Snigdha OS. All rights reserved.</p>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,84 @@
import { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { Menu, X } from 'lucide-react';
import { cn } from '@/lib/utils';
import { navigation } from '@/data/navigation';
function Logo() {
return (
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 2L3 9V23L16 30L29 23V9L16 2Z" stroke="#6495ED" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
<path d="M16 2V16M16 16V30M16 16L29 9M16 16L3 9" stroke="#6495ED" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
);
}
export function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const location = useLocation();
return (
<nav className="fixed w-full z-50 bg-white/80 backdrop-blur-lg border-b border-gray-200">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
<div className="flex">
<Link to="/" className="flex-shrink-0 flex items-center">
<Logo />
<span className="ml-2 text-xl font-bold text-gray-900">Snigdha OS</span>
</Link>
</div>
<div className="hidden sm:flex sm:items-center sm:space-x-8">
{navigation.map((item) => (
<Link
key={item.name}
to={item.href}
className={cn(
'px-3 py-2 rounded-md text-sm font-medium transition-colors flex items-center gap-2',
location.pathname === item.href
? 'text-cornflower-blue'
: 'text-gray-600 hover:text-cornflower-blue'
)}
>
<item.icon className="h-4 w-4" />
{item.name}
</Link>
))}
</div>
<div className="flex items-center sm:hidden">
<button
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100"
>
{isOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
</button>
</div>
</div>
</div>
{isOpen && (
<div className="sm:hidden bg-white/90 backdrop-blur-lg">
<div className="px-2 pt-2 pb-3 space-y-1">
{navigation.map((item) => (
<Link
key={item.name}
to={item.href}
className={cn(
'block px-3 py-2 rounded-md text-base font-medium flex items-center gap-2',
location.pathname === item.href
? 'text-cornflower-blue bg-blue-50'
: 'text-gray-600 hover:text-cornflower-blue hover:bg-blue-50'
)}
onClick={() => setIsOpen(false)}
>
<item.icon className="h-4 w-4" />
{item.name}
</Link>
))}
</div>
</div>
)}
</nav>
);
}

View File

@@ -0,0 +1,34 @@
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
import { AlertCircle } from 'lucide-react';
function ErrorFallback({ error }: { error: Error }) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="max-w-md w-full p-6 bg-white/80 backdrop-blur-lg rounded-lg shadow-lg">
<div className="flex items-center justify-center text-red-500 mb-4">
<AlertCircle size={48} />
</div>
<h2 className="text-2xl font-bold text-gray-900 text-center mb-4">
Something went wrong
</h2>
<pre className="text-sm bg-gray-100 p-4 rounded overflow-auto">
{error.message}
</pre>
<button
onClick={() => window.location.reload()}
className="mt-4 w-full bg-cornflower-blue text-white py-2 px-4 rounded hover:bg-blue-600 transition-colors"
>
Try again
</button>
</div>
</div>
);
}
export function ErrorBoundary({ children }: { children: React.ReactNode }) {
return (
<ReactErrorBoundary FallbackComponent={ErrorFallback}>
{children}
</ReactErrorBoundary>
);
}

56
src/data/donations.ts Normal file
View File

@@ -0,0 +1,56 @@
export const donationTiers = [
{
name: 'Supporter',
amount: 10,
benefits: [
'Name on donor wall',
'Special Discord role',
'Monthly newsletter',
],
},
{
name: 'Contributor',
amount: 25,
benefits: [
'Name on donor wall',
'Special Discord role',
'Monthly newsletter',
'Early access to releases',
'Exclusive wallpapers',
],
recommended: true,
},
{
name: 'Sustainer',
amount: 50,
benefits: [
'Name on donor wall',
'Special Discord role',
'Monthly newsletter',
'Early access to releases',
'Exclusive wallpapers',
'Private support channel',
'Vote on future features',
],
},
];
export const recentDonors = [
{
name: 'Alice Johnson',
amount: 25,
message: 'Keep up the amazing work!',
date: '2024-03-15',
},
{
name: 'Bob Smith',
amount: 50,
date: '2024-03-14',
},
{
name: 'Carol Williams',
amount: 10,
message: 'Love using Snigdha OS for security research',
date: '2024-03-13',
},
];

17
src/data/download.ts Normal file
View File

@@ -0,0 +1,17 @@
export interface DownloadVersion {
version: string;
size: string;
url: string;
sha256: string;
gpg: string;
}
export const downloads: DownloadVersion[] = [
{
version: '2024.1',
size: '4.2 GB',
url: 'https://snigdhaos.org/downloads/snigdhaos-2024.1-installer-amd64.iso',
sha256: 'e4654e5633f4e1f8f57a9fb3dca02f9db06e9acb5e346f0fae9d9f5c3a9c0e9',
gpg: '-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n...',
},
];

View File

@@ -0,0 +1,15 @@
import { Github, Twitter, Youtube, Book, MessageSquare, Newspaper, HelpCircle } from 'lucide-react';
export const footerNavigation = {
resources: [
{ name: 'Documentation', href: '/docs', icon: Book },
{ name: 'Blog', href: '/blog', icon: Newspaper },
{ name: 'Support', href: '/support', icon: HelpCircle },
],
community: [
{ name: 'Community', href: '/community', icon: MessageSquare },
{ name: 'GitHub', href: 'https://github.com/Snigdha-OS', icon: Github },
{ name: 'Twitter', href: 'https://twitter.com', icon: Twitter },
{ name: 'YouTube', href: 'https://youtube.com', icon: Youtube },
],
};

34
src/data/gallery.ts Normal file
View File

@@ -0,0 +1,34 @@
export const galleryImages = [
{
src: 'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5',
alt: 'Security Dashboard',
category: 'Interface',
},
{
src: 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8',
alt: 'Network Analysis',
category: 'Tools',
},
{
src: 'https://images.unsplash.com/photo-1555949963-ff9fe0c870eb',
alt: 'Terminal Environment',
category: 'Interface',
},
{
src: 'https://images.unsplash.com/photo-1526374870839-e155464bb9b2',
alt: 'Security Testing',
category: 'Tools',
},
{
src: 'https://images.unsplash.com/photo-1551808525-51a94da548ce',
alt: 'Hardware Support',
category: 'Hardware',
},
{
src: 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31',
alt: 'Wireless Testing',
category: 'Hardware',
},
];
export const categories = ['All', 'Interface', 'Tools', 'Hardware'];

25
src/data/mirrors.ts Normal file
View File

@@ -0,0 +1,25 @@
import { Mirror } from '@/types/download';
export const mirrors: Mirror[] = [
{
id: 'us-east',
name: 'US East Mirror',
location: 'New York, USA',
url: 'https://mirror-east.snigdhaos.org',
speed: 120,
},
{
id: 'eu-central',
name: 'EU Central Mirror',
location: 'Frankfurt, Germany',
url: 'https://mirror-eu.snigdhaos.org',
speed: 100,
},
{
id: 'asia-east',
name: 'Asia East Mirror',
location: 'Singapore',
url: 'https://mirror-asia.snigdhaos.org',
speed: 80,
},
];

View File

@@ -0,0 +1,25 @@
import { type Mirror } from '@/types/download';
export const africanMirrors: Mirror[] = [
{
id: 'za-cape',
name: 'South Africa Mirror',
location: 'Cape Town, South Africa',
url: 'https://mirror-za.snigdhaos.org',
speed: 75,
},
{
id: 'eg-cairo',
name: 'Egypt Mirror',
location: 'Cairo, Egypt',
url: 'https://mirror-eg.snigdhaos.org',
speed: 65,
},
{
id: 'ke-nairobi',
name: 'Kenya Mirror',
location: 'Nairobi, Kenya',
url: 'https://mirror-ke.snigdhaos.org',
speed: 60,
},
];

View File

@@ -0,0 +1,32 @@
import { type Mirror } from '@/types/download';
export const americasMirrors: Mirror[] = [
{
id: 'us-east',
name: 'US East Mirror',
location: 'New York, USA',
url: 'https://mirror-us-east.snigdhaos.org',
speed: 130,
},
{
id: 'us-west',
name: 'US West Mirror',
location: 'San Francisco, USA',
url: 'https://mirror-us-west.snigdhaos.org',
speed: 125,
},
{
id: 'ca-central',
name: 'Canada Mirror',
location: 'Toronto, Canada',
url: 'https://mirror-ca.snigdhaos.org',
speed: 115,
},
{
id: 'br-sao',
name: 'Brazil Mirror',
location: 'São Paulo, Brazil',
url: 'https://mirror-br.snigdhaos.org',
speed: 90,
},
];

32
src/data/mirrors/asia.ts Normal file
View File

@@ -0,0 +1,32 @@
import { type Mirror } from '@/types/download';
export const asianMirrors: Mirror[] = [
{
id: 'sg-central',
name: 'Singapore Mirror',
location: 'Singapore',
url: 'https://mirror-sg.snigdhaos.org',
speed: 120,
},
{
id: 'jp-tokyo',
name: 'Japan Mirror',
location: 'Tokyo, Japan',
url: 'https://mirror-jp.snigdhaos.org',
speed: 150,
},
{
id: 'in-mumbai',
name: 'India Mirror',
location: 'Mumbai, India',
url: 'https://mirror-in.snigdhaos.org',
speed: 85,
},
{
id: 'kr-seoul',
name: 'South Korea Mirror',
location: 'Seoul, South Korea',
url: 'https://mirror-kr.snigdhaos.org',
speed: 140,
},
];

View File

@@ -0,0 +1,32 @@
import { type Mirror } from '@/types/download';
export const europeanMirrors: Mirror[] = [
{
id: 'de-frankfurt',
name: 'Germany Mirror',
location: 'Frankfurt, Germany',
url: 'https://mirror-de.snigdhaos.org',
speed: 130,
},
{
id: 'uk-london',
name: 'UK Mirror',
location: 'London, UK',
url: 'https://mirror-uk.snigdhaos.org',
speed: 125,
},
{
id: 'fr-paris',
name: 'France Mirror',
location: 'Paris, France',
url: 'https://mirror-fr.snigdhaos.org',
speed: 120,
},
{
id: 'nl-amsterdam',
name: 'Netherlands Mirror',
location: 'Amsterdam, Netherlands',
url: 'https://mirror-nl.snigdhaos.org',
speed: 135,
},
];

14
src/data/mirrors/index.ts Normal file
View File

@@ -0,0 +1,14 @@
import { africanMirrors } from './africa';
import { americasMirrors } from './americas';
import { asianMirrors } from './asia';
import { europeanMirrors } from './europe';
import { oceaniaMirrors } from './oceania';
import { type Mirror } from '@/types/download';
export const mirrors: Mirror[] = [
...americasMirrors,
...europeanMirrors,
...asianMirrors,
...oceaniaMirrors,
...africanMirrors,
];

View File

@@ -0,0 +1,18 @@
import { type Mirror } from '@/types/download';
export const oceaniaMirrors: Mirror[] = [
{
id: 'au-sydney',
name: 'Australia Mirror',
location: 'Sydney, Australia',
url: 'https://mirror-au.snigdhaos.org',
speed: 100,
},
{
id: 'nz-auckland',
name: 'New Zealand Mirror',
location: 'Auckland, New Zealand',
url: 'https://mirror-nz.snigdhaos.org',
speed: 95,
},
];

11
src/data/navigation.ts Normal file
View File

@@ -0,0 +1,11 @@
import { Home, Info, Layout, Image, Download, Code, Heart } from 'lucide-react';
export const navigation = [
{ name: 'Home', href: '/', icon: Home },
{ name: 'About', href: '/about', icon: Info },
{ name: 'Features', href: '/features', icon: Layout },
{ name: 'Gallery', href: '/gallery', icon: Image },
{ name: 'Download', href: '/download', icon: Download },
{ name: 'Developers', href: '/developers', icon: Code },
{ name: 'Donate', href: '/donate', icon: Heart },
];

17
src/data/team.ts Normal file
View File

@@ -0,0 +1,17 @@
export const teamMembers = [
{
username: 'eshanized', // Using placeholder usernames that exist on GitHub
role: 'Core Development',
description: 'Leading the development of Snigdha OS core system and architecture',
},
{
username: 'd3v1l0n',
role: 'Security Tools',
description: 'Managing security tools integration and package maintenance',
},
{
username: 'alokified',
role: 'Project Lead',
description: 'Overseeing project direction and community engagement',
},
];

17
src/data/testimonials.ts Normal file
View File

@@ -0,0 +1,17 @@
export const testimonials = [
{
content: "Snigdha OS has revolutionized our security testing workflow. The tools and interface are exceptional.",
author: "Sarah Chen",
role: "Security Engineer at TechCorp"
},
{
content: "The best security-focused distribution I've used. Regular updates and great community support.",
author: "Michael Rodriguez",
role: "Penetration Tester"
},
{
content: "Perfect for both beginners and advanced users. The documentation is comprehensive and helpful.",
author: "Emma Thompson",
role: "Cybersecurity Consultant"
}
];

62
src/data/tools.ts Normal file
View File

@@ -0,0 +1,62 @@
export interface Tool {
id: string;
name: string;
description: string;
category: string;
command: string;
}
export const tools: Tool[] = [
{
id: '1',
name: 'Nmap',
description: 'Network exploration tool and security scanner',
category: 'Information Gathering',
command: 'nmap',
},
{
id: '2',
name: 'Wireshark',
description: 'Network protocol analyzer for real-time packet capture',
category: 'Sniffing & Spoofing',
command: 'wireshark',
},
{
id: '3',
name: 'Metasploit',
description: 'Penetration testing framework',
category: 'Exploitation Tools',
command: 'msfconsole',
},
{
id: '4',
name: 'Burp Suite',
description: 'Web vulnerability scanner and proxy tool',
category: 'Web Applications',
command: 'burpsuite',
},
{
id: '5',
name: 'Aircrack-ng',
description: 'Complete suite for wireless network security assessment',
category: 'Wireless Attacks',
command: 'aircrack-ng',
},
{
id: '6',
name: 'John the Ripper',
description: 'Password cracker and hash analyzer',
category: 'Password Attacks',
command: 'john',
},
];
export const categories = [
'All',
'Information Gathering',
'Sniffing & Spoofing',
'Exploitation Tools',
'Web Applications',
'Wireless Attacks',
'Password Attacks',
];

25
src/hooks/useLocation.ts Normal file
View File

@@ -0,0 +1,25 @@
import { useState, useEffect } from 'react';
import { getUserLocation, type UserLocation } from '@/lib/location';
export function useLocation() {
const [location, setLocation] = useState<UserLocation | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
async function fetchLocation() {
try {
const userLocation = await getUserLocation();
setLocation(userLocation);
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to get location'));
} finally {
setIsLoading(false);
}
}
fetchLocation();
}, []);
return { location, isLoading, error };
}

View File

@@ -0,0 +1,25 @@
import { useState, useEffect } from 'react';
import { measureNetworkSpeed } from '@/lib/network';
export function useNetworkSpeed() {
const [speed, setSpeed] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
async function checkSpeed() {
try {
const measuredSpeed = await measureNetworkSpeed();
setSpeed(measuredSpeed);
} catch (err) {
setError(err instanceof Error ? err : new Error('Failed to measure network speed'));
} finally {
setIsLoading(false);
}
}
checkSpeed();
}, []);
return { speed, isLoading, error };
}

7
src/index.css Normal file
View File

@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
font-family: 'Fira Sans', sans-serif;
}

8
src/lib/currency.ts Normal file
View File

@@ -0,0 +1,8 @@
export function formatINR(amount: number) {
return new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(amount);
}

57
src/lib/github.ts Normal file
View File

@@ -0,0 +1,57 @@
const GITHUB_API_URL = 'https://api.github.com';
export interface GithubUser {
login: string;
name: string | null;
avatar_url: string;
html_url: string;
bio: string | null;
public_repos: number;
followers: number;
location: string | null;
}
export interface GithubRepo {
id: number;
name: string;
description: string;
html_url: string;
stargazers_count: number;
forks_count: number;
language: string;
updated_at: string;
}
export async function fetchGithubUser(username: string): Promise<GithubUser> {
try {
const response = await fetch(`${GITHUB_API_URL}/users/${username}`);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
console.error(`GitHub API Error (${response.status}):`, errorData);
throw new Error(`Failed to fetch user ${username}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching GitHub user ${username}:`, error);
throw error;
}
}
export async function fetchOrgRepos(org: string): Promise<GithubRepo[]> {
try {
const response = await fetch(`${GITHUB_API_URL}/orgs/${org}/repos?sort=updated&per_page=100`);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
console.error(`GitHub API Error (${response.status}):`, errorData);
throw new Error(`Failed to fetch repositories: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching repositories:', error);
throw error;
}
}

18
src/lib/location.ts Normal file
View File

@@ -0,0 +1,18 @@
export interface UserLocation {
country: string;
city: string;
continent: string;
}
export async function getUserLocation(): Promise<UserLocation> {
const response = await fetch('https://ipapi.co/json/');
if (!response.ok) {
throw new Error('Failed to fetch location');
}
const data = await response.json();
return {
country: data.country_name,
city: data.city,
continent: data.continent_code,
};
}

18
src/lib/network.ts Normal file
View File

@@ -0,0 +1,18 @@
export async function measureNetworkSpeed(): Promise<number> {
const startTime = performance.now();
const response = await fetch('https://www.cloudflare.com/cdn-cgi/trace', { cache: 'no-store' });
const endTime = performance.now();
const duration = endTime - startTime;
if (!response.ok) {
throw new Error('Failed to measure network speed');
}
const data = await response.text();
const size = new Blob([data]).size;
// Calculate speed in Mbps (megabits per second)
const speedMbps = (size * 8) / (duration / 1000) / 1000000;
return Math.round(speedMbps * 100) / 100;
}

18
src/lib/utils.ts Normal file
View File

@@ -0,0 +1,18 @@
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(date: string) {
return new Date(date).toLocaleDateString('en-US', {
month: 'long',
day: 'numeric',
year: 'numeric',
});
}
export function formatSpeed(speed: number) {
return `${speed} Mbps`;
}

10
src/main.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
);

39
src/pages/About.tsx Normal file
View File

@@ -0,0 +1,39 @@
import { motion } from 'framer-motion';
import { Timeline } from '@/components/about/Timeline';
import { TeamSection } from '@/components/about/TeamSection';
import { MissionSection } from '@/components/about/MissionSection';
export default function About() {
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<h1 className="text-4xl font-bold text-gray-900">About Snigdha OS</h1>
<p className="mt-4 text-lg text-gray-600">
The most advanced penetration testing distribution, made for security professionals.
</p>
</motion.div>
<div className="space-y-16">
<section>
<MissionSection />
</section>
<section>
<h2 className="text-2xl font-bold text-gray-900 mb-8">Our Team Structure</h2>
<TeamSection />
</section>
<section>
<h2 className="text-2xl font-bold text-gray-900 mb-8">Release Timeline</h2>
<Timeline />
</section>
</div>
</div>
</div>
);
}

84
src/pages/Developers.tsx Normal file
View File

@@ -0,0 +1,84 @@
import { useQueries, useQuery } from '@tanstack/react-query';
import { motion } from 'framer-motion';
import { Loader2 } from 'lucide-react';
import { TeamMemberCard } from '@/components/developers/TeamMemberCard';
import { RepoCard } from '@/components/developers/RepoCard';
import { fetchGithubUser, fetchOrgRepos } from '@/lib/github';
import { teamMembers } from '@/data/team';
export default function Developers() {
const queries = useQueries({
queries: teamMembers.map(member => ({
queryKey: ['github-user', member.username],
queryFn: () => fetchGithubUser(member.username),
})),
});
const { data: repos, isLoading: loadingRepos, error: reposError } = useQuery({
queryKey: ['github-repos', 'Snigdha-OS'],
queryFn: () => fetchOrgRepos('Snigdha-OS'),
});
const isLoading = queries.some(query => query.isLoading) || loadingRepos;
const isError = queries.some(query => query.isError) || reposError;
if (isLoading) {
return (
<div className="min-h-[50vh] flex items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-cornflower-blue" />
</div>
);
}
if (isError) {
return (
<div className="min-h-[50vh] flex items-center justify-center">
<p className="text-red-500">Failed to load data</p>
</div>
);
}
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<h1 className="text-4xl font-bold text-gray-900">Our Team</h1>
<p className="mt-4 text-lg text-gray-600">
Meet the dedicated team behind Snigdha OS
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
{queries.map((query, index) => (
query.data && (
<TeamMemberCard
key={query.data.login}
user={query.data}
role={teamMembers[index].role}
description={teamMembers[index].description}
/>
)
))}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.2 }}
className="mt-16"
>
<h2 className="text-3xl font-bold text-gray-900 mb-8">Our Repositories</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{repos?.map((repo) => (
<RepoCard key={repo.id} repo={repo} />
))}
</div>
</motion.div>
</div>
</div>
);
}

58
src/pages/Donate.tsx Normal file
View File

@@ -0,0 +1,58 @@
import { motion } from 'framer-motion';
import { Heart } from 'lucide-react';
import { GithubSponsorButton } from '@/components/donate/GithubSponsorButton';
import { SponsorshipTiers } from '@/components/donate/SponsorshipTiers';
import { SponsorshipStats } from '@/components/donate/SponsorshipStats';
import { KeySponsors } from '@/components/donate/KeySponsors';
export default function Donate() {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-50 to-white py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<Heart className="h-12 w-12 text-red-500 mx-auto mb-4" />
<h1 className="text-4xl font-bold text-gray-900">Support Snigdha OS</h1>
<p className="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">
Your support helps us maintain and improve Snigdha OS, keeping it free and open source for the security community.
</p>
<div className="mt-8">
<GithubSponsorButton />
</div>
</motion.div>
<div className="space-y-16">
<section>
<KeySponsors />
</section>
<section>
<SponsorshipStats />
</section>
<section>
<h2 className="text-2xl font-bold text-gray-900 text-center mb-8">
Choose Your Sponsorship Tier
</h2>
<SponsorshipTiers />
</section>
<section className="max-w-3xl mx-auto text-center bg-cornflower-blue/5 rounded-2xl p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-4">
Why Sponsor Snigdha OS?
</h2>
<p className="text-gray-600">
Your sponsorship directly supports the development of cutting-edge security tools,
maintenance of our infrastructure, and helps us keep Snigdha OS at the forefront
of security testing. Join our community of sponsors and help shape the future of
security testing.
</p>
</section>
</div>
</div>
</div>
);
}

95
src/pages/Download.tsx Normal file
View File

@@ -0,0 +1,95 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Loader2 } from 'lucide-react';
import { DownloadButton } from '@/components/download/DownloadButton';
import { SystemRequirements } from '@/components/download/SystemRequirements';
import { Checksum } from '@/components/download/Checksum';
import { MirrorList } from '@/components/download/MirrorList';
import { NetworkSpeed } from '@/components/download/NetworkSpeed';
import { SuggestedMirror } from '@/components/download/SuggestedMirror';
import { downloads } from '@/data/download';
import { mirrors } from '@/data/mirrors';
import { type Mirror } from '@/types/download';
import { useNetworkSpeed } from '@/hooks/useNetworkSpeed';
import { useLocation } from '@/hooks/useLocation';
export default function Download() {
const latestVersion = downloads[0];
const [selectedMirror, setSelectedMirror] = useState<Mirror | null>(null);
const { speed, isLoading: loadingSpeed } = useNetworkSpeed();
const { location, isLoading: loadingLocation } = useLocation();
const handleMirrorSelect = (mirror: Mirror) => {
setSelectedMirror(mirror);
};
// Sort mirrors by speed and location
const sortedMirrors = [...mirrors].sort((a, b) => {
if (!speed) return 0;
const aDiff = Math.abs(a.speed - speed);
const bDiff = Math.abs(b.speed - speed);
return aDiff - bDiff;
});
// Get suggested mirror based on location and speed
const suggestedMirror = location ? sortedMirrors[0] : null;
if (loadingSpeed || loadingLocation) {
return (
<div className="min-h-[50vh] flex items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-cornflower-blue" />
</div>
);
}
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<h1 className="text-4xl font-bold text-gray-900">Download Snigdha OS</h1>
<p className="mt-4 text-lg text-gray-600">
Get the latest version of the most advanced penetration testing distribution
</p>
</motion.div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2 space-y-8">
<DownloadButton {...latestVersion} />
<div>
<h2 className="text-xl font-semibold text-gray-900 mb-4">Download Mirrors</h2>
<p className="text-sm text-gray-600 mb-4">
Mirrors are sorted by compatibility with your connection speed for optimal download performance
</p>
<MirrorList
mirrors={sortedMirrors}
onSelect={handleMirrorSelect}
/>
</div>
<Checksum
sha256={latestVersion.sha256}
gpg={latestVersion.gpg}
/>
</div>
<div className="space-y-6">
{speed !== null && <NetworkSpeed speed={speed} />}
{location && suggestedMirror && (
<SuggestedMirror
mirror={suggestedMirror}
userLocation={location}
onSelect={handleMirrorSelect}
/>
)}
<SystemRequirements />
</div>
</div>
</div>
</div>
);
}

64
src/pages/Features.tsx Normal file
View File

@@ -0,0 +1,64 @@
import { useState, useMemo } from 'react';
import { motion } from 'framer-motion';
import { SearchBar } from '@/components/features/SearchBar';
import { CategoryFilter } from '@/components/features/CategoryFilter';
import { ToolCard } from '@/components/features/ToolCard';
import { tools, categories } from '@/data/tools';
export default function Features() {
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState('All');
const filteredTools = useMemo(() => {
return tools.filter((tool) => {
const matchesSearch = tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
tool.description.toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = selectedCategory === 'All' || tool.category === selectedCategory;
return matchesSearch && matchesCategory;
});
}, [searchQuery, selectedCategory]);
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<h1 className="text-4xl font-bold text-gray-900">Snigdha OS Tools</h1>
<p className="mt-4 text-lg text-gray-600">
Explore our comprehensive collection of security and penetration testing tools
</p>
</motion.div>
<div className="space-y-8">
<div className="max-w-xl mx-auto">
<SearchBar
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<CategoryFilter
categories={categories}
selectedCategory={selectedCategory}
onSelect={setSelectedCategory}
/>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredTools.map((tool) => (
<ToolCard key={tool.id} {...tool} />
))}
</div>
{filteredTools.length === 0 && (
<div className="text-center py-12">
<p className="text-gray-500">No tools found matching your criteria</p>
</div>
)}
</div>
</div>
</div>
);
}

46
src/pages/Gallery.tsx Normal file
View File

@@ -0,0 +1,46 @@
import { useState, useMemo } from 'react';
import { motion } from 'framer-motion';
import { Camera } from 'lucide-react';
import { GalleryImage } from '@/components/gallery/GalleryImage';
import { CategoryFilter } from '@/components/gallery/CategoryFilter';
import { galleryImages, categories } from '@/data/gallery';
export default function Gallery() {
const [selectedCategory, setSelectedCategory] = useState('All');
const filteredImages = useMemo(() => {
return selectedCategory === 'All'
? galleryImages
: galleryImages.filter((image) => image.category === selectedCategory);
}, [selectedCategory]);
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-center mb-12"
>
<Camera className="h-12 w-12 text-cornflower-blue mx-auto mb-4" />
<h1 className="text-4xl font-bold text-gray-900">Gallery</h1>
<p className="mt-4 text-lg text-gray-600">
Explore the visual journey of Snigdha OS
</p>
</motion.div>
<CategoryFilter
categories={categories}
selectedCategory={selectedCategory}
onSelect={setSelectedCategory}
/>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredImages.map((image) => (
<GalleryImage key={image.src} {...image} />
))}
</div>
</div>
</div>
);
}

84
src/pages/Home.tsx Normal file
View File

@@ -0,0 +1,84 @@
import { Shield, Terminal, Cpu } from 'lucide-react';
import { HeroSection } from '@/components/home/HeroSection';
import { FeatureCard } from '@/components/home/FeatureCard';
import { StatsSection } from '@/components/home/StatsSection';
import { ToolsShowcase } from '@/components/home/ToolsShowcase';
import { TestimonialCard } from '@/components/home/TestimonialCard';
import { ComparisonSection } from '@/components/home/ComparisonSection';
import { testimonials } from '@/data/testimonials';
const features = [
{
title: 'Advanced Security Tools',
description: 'Access over 600 pre-installed security and penetration testing tools, ready to use out of the box.',
icon: Shield,
},
{
title: 'Powerful Terminal',
description: 'Enhanced command-line interface with custom tools and utilities for efficient security testing.',
icon: Terminal,
},
{
title: 'Hardware Compatibility',
description: 'Optimized for various hardware configurations with excellent driver support.',
icon: Cpu,
},
];
export default function Home() {
return (
<div className="bg-gray-50">
<HeroSection />
<section className="py-20 bg-gradient-to-b from-gray-900 to-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-16">
<h2 className="text-3xl font-bold text-white sm:text-4xl">
Powerful Features
</h2>
<p className="mt-4 text-lg text-gray-300">
Everything you need for professional security testing
</p>
</div>
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
{features.map((feature, index) => (
<FeatureCard
key={feature.title}
{...feature}
delay={index * 0.2}
/>
))}
</div>
</div>
</section>
<ToolsShowcase />
<ComparisonSection />
<section className="py-20 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-gray-900">What Users Say</h2>
<p className="mt-4 text-lg text-gray-600">
Trusted by security professionals worldwide
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{testimonials.map((testimonial, index) => (
<TestimonialCard
key={testimonial.author}
{...testimonial}
delay={index * 0.2}
/>
))}
</div>
</div>
</section>
<StatsSection />
</div>
);
}

35
src/routes.tsx Normal file
View File

@@ -0,0 +1,35 @@
import { Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';
import { Loader2 } from 'lucide-react';
const HomePage = lazy(() => import('@/pages/Home'));
const AboutPage = lazy(() => import('@/pages/About'));
const FeaturesPage = lazy(() => import('@/pages/Features'));
const DownloadPage = lazy(() => import('@/pages/Download'));
const DevelopersPage = lazy(() => import('@/pages/Developers'));
const DonatePage = lazy(() => import('@/pages/Donate'));
const GalleryPage = lazy(() => import('@/pages/Gallery'));
function LoadingSpinner() {
return (
<div className="min-h-[50vh] flex items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-cornflower-blue" />
</div>
);
}
export function AppRoutes() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/features" element={<FeaturesPage />} />
<Route path="/download" element={<DownloadPage />} />
<Route path="/developers" element={<DevelopersPage />} />
<Route path="/donate" element={<DonatePage />} />
<Route path="/gallery" element={<GalleryPage />} />
</Routes>
</Suspense>
);
}

15
src/types/download.ts Normal file
View File

@@ -0,0 +1,15 @@
export interface Mirror {
id: string;
name: string;
location: string;
url: string;
speed: number;
}
export interface DownloadVersion {
version: string;
size: string;
url: string;
sha256: string;
gpg: string;
}

10
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1,10 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_GITHUB_TOKEN: string
readonly VITE_STRIPE_PUBLIC_KEY: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}

30
tailwind.config.js Normal file
View File

@@ -0,0 +1,30 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
'cornflower-blue': '#6495ED',
},
animation: {
'gradient': 'gradient 8s linear infinite',
},
keyframes: {
gradient: {
'0%, 100%': {
'background-size': '200% 200%',
'background-position': 'left center',
},
'50%': {
'background-size': '200% 200%',
'background-position': 'right center',
},
},
},
fontFamily: {
'fira-sans': ['"Fira Sans"', 'sans-serif'],
},
},
},
plugins: [],
};

24
tsconfig.app.json Normal file
View File

@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

22
tsconfig.node.json Normal file
View File

@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}

15
vite.config.ts Normal file
View File

@@ -0,0 +1,15 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': resolve(__dirname, './src'),
},
},
optimizeDeps: {
exclude: ['lucide-react'],
},
});