Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
733 changes: 732 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"prismjs": "^1.30.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-google-recaptcha": "^3.1.0",
"react-hot-toast": "^2.6.0",
"react-simple-code-editor": "^0.14.1",
"socket.io": "^4.8.1",
Expand All @@ -45,6 +46,7 @@
"@types/prismjs": "^1.26.5",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react-google-recaptcha": "^2.1.9",
"tailwindcss": "^4",
"typescript": "^5"
}
Expand Down
1,488 changes: 835 additions & 653 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions src/api/verify/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

export async function POST(request: Request) {
try {
const { token } = await request.json()

const verificationURL = 'https://www.google.com/recaptcha/api/siteverify'
const secretKey = process.env.RECAPTCHA_SECRET_KEY

const verificationResponse = await fetch(verificationURL, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
secret: secretKey || '',
response: token,
}).toString(),
})

const data = await verificationResponse.json()

if (data.success) {
const cookieStore = await cookies()
cookieStore.set('recaptcha-verified', 'true', {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 3600
})

return NextResponse.json({ success: true })
}

return NextResponse.json({ success: false }, { status: 400 })
} catch (error) {
console.error('Verification error:', error)
return NextResponse.json({ success: false, error: 'Internal server error' }, { status: 500 })
}
}
73 changes: 73 additions & 0 deletions src/app/verify/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
'use client'

import { useRef, useState, useEffect } from 'react'
import ReCAPTCHA from 'react-google-recaptcha'
import { useRouter } from 'next/navigation'

export default function VerifyPage() {
const router = useRouter()
const recaptchaRef = useRef<ReCAPTCHA>(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [isMounted, setIsMounted] = useState(false)

useEffect(() => {
setIsMounted(true)
}, [])

const handleVerify = async (token: string | null) => {
if (!token) return

setIsLoading(true)
setError(null)

try {
const response = await fetch('/api/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
})

if (response.ok) {
router.push('/Collaborate')
} else {
const data = await response.json()
setError(data.error || 'Verification failed')
}
} catch (error) {
console.error('Verification failed:', error)
setError('Network error occurred')
} finally {
setIsLoading(false)
}
}

if (!isMounted) {
return <div>Loading...</div>
}

return (
<div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
<div className="p-8 bg-white rounded-lg shadow-lg w-full max-w-md">
<h1 className="text-2xl font-bold mb-6 text-center">Verify You're Human</h1>
<div className="flex justify-center mb-4">
{process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY ? (
<ReCAPTCHA
ref={recaptchaRef}
sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}
onChange={handleVerify}
/>
) : (
<p className="text-red-500">ReCAPTCHA site key not configured</p>
)}
</div>
{isLoading && (
<p className="text-center text-gray-600">Verifying...</p>
)}
{error && (
<p className="text-center text-red-500">{error}</p>
)}
</div>
</div>
)
}
36 changes: 13 additions & 23 deletions src/components/error-section7.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,28 @@

import React from "react";
import Link from "next/link";
import { Typography} from "@material-tailwind/react";
import { Button } from "@/components/ui/button";
import { FlagIcon } from "@heroicons/react/24/solid";

export function ErrorSection7() {
return (
<div className="h-screen mx-auto grid place-items-center text-center px-8">
<div>
<FlagIcon className="w-20 h-20 mx-auto" />
<Typography
variant="h1"
className="mt-10 text-blue-gray-700 !text-3xl !leading-snug md:!text-4xl"
placeholder="Enter text here" // Example placeholder
onPointerEnterCapture={() => {}} // Example event handler
onPointerLeaveCapture={() => {}}
>
Error 404 <br /> It looks like something went wrong.
</Typography>
<Typography className="mt-8 mb-14 text-[18px] font-normal text-gray-500 mx-auto md:max-w-sm"
placeholder="Enter text here" // Example placeholder
onPointerEnterCapture={() => {}} // Example event handler
onPointerLeaveCapture={() => {}}>
Don&apos;t worry, our team is already on it.Please try refreshing
the page or come back later.
</Typography>
<Link href="/" passHref>
<div className="h-screen mx-auto grid place-items-center text-center px-8">
<div>
<FlagIcon className="w-20 h-20 mx-auto" />
<h1 className="mt-10 text-blue-gray-700 text-3xl leading-snug md:text-4xl font-bold">
Error 404 <br /> It looks like something went wrong.
</h1>
<p className="mt-8 mb-14 text-[18px] font-normal text-gray-500 mx-auto md:max-w-sm">
Don&apos;t worry, our team is already on it. Please try refreshing
the page or come back later.
</p>
<Link href="/" passHref>
<Button className="w-full px-4 md:w-[8rem] cursor-pointer">
Back To Home
</Button>
</Link>
</div>
</Link>
</div>
</div>
);
}

Expand Down
20 changes: 20 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname

if (pathname === '/Collaborate') {
const verificationToken = request.cookies.get('recaptcha-verified')

if (!verificationToken) {
return NextResponse.redirect(new URL('/verify', request.url))
}
}

return NextResponse.next()
}

export const config = {
matcher: '/Collaborate'
}
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"dom.iterable",
"esnext"
],
"types": ["@material-tailwind/react"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
Expand Down