Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Google reCAPTCHA to pbctf registration #74

Merged
merged 3 commits into from
Sep 22, 2024
Merged
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
64 changes: 64 additions & 0 deletions app/(default)/api/registration/pbctf/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { db } from "@/Firebase";

import { recruitValidate } from "@/lib/server/utils";

import {
addDoc,
collection,
getDocs,
limit,
query,
where,
} from "firebase/firestore";
import { NextResponse } from "next/server";

// Add a new registration
export async function POST(request: Request) {
const formData = await request.json();
const { recaptcha_token } = formData;


const recaptchaToken = recaptcha_token;

const details = {
event: {
token: recaptchaToken,
siteKey: process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY,
},
};


if (!recaptchaToken) {
return NextResponse.json(
{
message: "reCAPTCHA token not found! Try again",
error: "reCAPTCHA token not found!",
},
{
status: 500,
}
);
}

// Verify the reCATPTCHA token

const recaptchaResponse = await fetch(
`https://recaptchaenterprise.googleapis.com/v1/projects/${process.env.RECAPTCHA_PROJECT}/assessments?key=${process.env.RECAPTCHA_API_KEY}`,
{
method: "POST",
body: JSON.stringify(details),
}
);

const recaptchaResult = await recaptchaResponse.json();
console.log(recaptchaResult.riskAnalysis.score);
if (recaptchaResult.riskAnalysis.score < 0.7) {
return NextResponse.json({
message: "reCAPTCHA validation failed",
error: recaptchaResult["error-codes"],
});
}

// Return a response
return NextResponse.json({ message: "Recaptcha validated!" });
}
14 changes: 7 additions & 7 deletions app/(default)/pbctf/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import { useRouter } from "next/navigation";
const PBCTFRegisterPage = () => {
const router = useRouter();

useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (!user) {
router.push("/login");
}
});
}, [router]);
// useEffect(() => {
// onAuthStateChanged(auth, (user) => {
// if (!user) {
// router.push("/login");
// }
// });
// }, [router]);

return (
<div className="min-h-screen flex items-center justify-center py-6 px-4 sm:px-6 lg:px-8 relative overflow-hidden">
Expand Down
20 changes: 10 additions & 10 deletions app/(default)/recruitment/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import { useRouter } from "next/navigation";
const RegisterPage = () => {
const router = useRouter();

// useEffect(() => {
// onAuthStateChanged(auth, (user) => {
// if (!user) {
// router.push("/login");
// }
// });
// });\
// useEffect(() => {
// router.push("/");
// })
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (!user) {
router.push("/login");
}
});
});
useEffect(() => {
router.push("/");
})
return (
<div className="w-50 mt-16 mx-auto flex flex-col items-center justify-center">
<div className="form-container my-2">
Expand Down
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function RootLayout({
<head>
<Script
src={`https://www.google.com/recaptcha/enterprise.js?render=${process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}`}
strategy="lazyOnload" // Set reCAPTCHA ready state when the script is loaded
strategy="beforeInteractive"
/>
</head>
<body
Expand Down
27 changes: 27 additions & 0 deletions components/forms/pbctfForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { collection, addDoc, query, where, getDocs } from "firebase/firestore";
import { db } from "@/Firebase";
import { branches } from "@/lib/constants/dropdownOptions";
import { Press_Start_2P } from "next/font/google";
import toast from "react-hot-toast";

const pressStart2P = Press_Start_2P({
weight: "400",
Expand All @@ -33,6 +34,7 @@ const PBCTFForm: React.FC = () => {
const [participationType, setParticipationType] = useState<"solo" | "duo">("solo");
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const [usnError, setUsnError] = useState<string | null>(null);
const [token, setToken] = useState<string | null>();

const [headingText, setHeadingText] = useState("");
const heading = "Be a Part of PBCTF Register Now!";
Expand Down Expand Up @@ -66,6 +68,10 @@ const PBCTFForm: React.FC = () => {
const watchUsn1 = watch("participant1.usn");
const watchUsn2 = watch("participant2.usn");

const setTokenFunc = (getToken: string) => {
setToken(getToken);
};

const checkUsnUniqueness = async (usn: string): Promise<boolean> => {
const q = query(collection(db, "pbctf_registrations"),
where("participant1.usn", "==", usn));
Expand All @@ -88,6 +94,27 @@ const PBCTFForm: React.FC = () => {
setUsnError(null);

try {

grecaptcha.enterprise.ready(async () => {
const token = await grecaptcha.enterprise.execute(
process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY
);
setTokenFunc(token);
});

const recaptcha_token = token;
const response = await fetch("/api/registration/pbctf", {
method: "POST",
body: JSON.stringify({recaptcha_token}),
});

const res = await response.json();

if (!response.ok || res.error) {
toast.error(res.message);
return;
}

// Check if USNs are the same for duo participation
if (data.participationType === "duo" && data.participant2 && data.participant1.usn === data.participant2.usn) {
setUsnError("USNs for Participant 1 and Participant 2 cannot be the same");
Expand Down
22 changes: 15 additions & 7 deletions components/forms/recruitmentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { useForm, SubmitHandler } from "react-hook-form";
import { years, branches } from "@/lib/constants/dropdownOptions";
import Success from "./success";
import toast from "react-hot-toast";
import { getErrorMessage, fetchCsrfToken } from "@/lib/client/clientUtils";
import { getErrorMessage } from "@/lib/client/clientUtils";

const RecruitmentForm: React.FC = () => {
const [isSuccess, setSuccess] = useState<boolean>(false);
const [mode, setMode] = useState<boolean>(false);
const [display, setDisplay] = useState<boolean>(false);
const [token, setToken] = useState("");

const [token, setToken] = useState<string | null>();
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);

const {
register,
Expand Down Expand Up @@ -41,6 +41,9 @@ const RecruitmentForm: React.FC = () => {
};

const onSubmit: SubmitHandler<any> = async (data: any) => {

if (isSubmitting) return;
setIsSubmitting(true);
try {
grecaptcha.enterprise.ready(async () => {
const token = await grecaptcha.enterprise.execute(
Expand Down Expand Up @@ -68,6 +71,8 @@ const RecruitmentForm: React.FC = () => {
} catch (error) {
console.error("Error submitting form:", error);
toast.error(getErrorMessage(error));
} finally {
setIsSubmitting(false);
}
};

Expand Down Expand Up @@ -232,10 +237,13 @@ const RecruitmentForm: React.FC = () => {
<input
{...register("whatsapp_number", {
required: "Whatsapp Number is required",
pattern: {
value: /^[6-9]\d{9}$/,
message: "Invalid phone number (10 digits starting with 6-9)",
},
})}
maxLength={10}
name="whatsapp_number"
type="tel"
pattern="[1-9]{1}[0-9]{9}"
placeholder="Enter your whatsapp number"
className="w-full px-4 py-2 border rounded-md bg-transparent form-input focus:border-0 focus:outline-offset-0 focus:outline-green-500"
/>
Expand Down Expand Up @@ -266,10 +274,10 @@ const RecruitmentForm: React.FC = () => {

<button
type="submit"
disabled={isSuccess}
disabled={isSubmitting}
className="bg-green-500 text-white rounded-lg py-2 px-4 hover:bg-green-600 "
>
Submit
{isSubmitting ? "Submitting..." : "Submit"}
</button>
</div>
</form>
Expand Down
76 changes: 30 additions & 46 deletions components/forms/success.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,50 @@


export default function Success(props: any) {
return (
<div className="w-full mx-auto">
<div className="flex flex-col p-5 rounded-lg shadow">

return(

<div className="w-full max-w-md mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex flex-col p-6 sm:p-8 rounded-lg shadow-lg bg-black bg-opacity-30 backdrop-blur-lg border border-green-500">
<div className="flex flex-col items-center text-center">
<div className="inline-block p-4 bg-green-300 rounded-full">
<div className="inline-block p-4 bg-green-500 rounded-full animate-bounce">
<svg
className="w-8 h-8 text-white"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0 0 64 64"
>
<circle cx="32" cy="32" r="23" fill="#98c900"></circle>
<path
fill="#fff"
d="M31.921,13.966c2.577,0,4.674-1.957,4.946-4.461c-1.594-0.349-3.247-0.539-4.946-0.539 c-12.703,0-23,10.297-23,23c0,1.699,0.19,3.352,0.539,4.946c2.505-0.272,4.461-2.369,4.461-4.946 C13.921,22.041,21.997,13.966,31.921,13.966z"
opacity=".3"
></path>
<path
d="M54.382,27.021c-2.505,0.272-4.461,2.369-4.461,4.946c0,9.925-8.075,18-18,18 c-2.577,0-4.674,1.957-4.946,4.461c1.594,0.349,3.247,0.539,4.946,0.539c12.703,0,23-10.297,23-23 C54.921,30.268,54.732,28.614,54.382,27.021z"
opacity=".15"
></path>
<path
fill="none"
stroke="#fff"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
stroke-width="3"
d="M14.968,23.393c1.878-3.699,4.932-6.705,8.666-8.522"
></path>
<ellipse cx="32" cy="61" opacity=".3" rx="19" ry="3"></ellipse>
<g>
<path
fill="#edff9c"
d="M29,42c-0.512,0-1.023-0.195-1.414-0.586l-7-7c-0.781-0.781-0.781-2.047,0-2.828 c0.781-0.781,2.047-0.781,2.828,0L29,37.171l12.586-12.585c0.781-0.781,2.047-0.781,2.828,0c0.781,0.781,0.781,2.047,0,2.828 l-14,14C30.023,41.805,29.512,42,29,42z"
></path>
</g>
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
</div>
<h2 className="mt-2 font-semibold text-gray-100">
Registration Successfull!
<h2 className="mt-4 text-2xl sm:text-3xl font-bold text-green-500">
Registration Successful!
</h2>
<p className="mt-2 text-sm text-gray-500 leading-relaxed">
{props.message}
<p className="mt-4 text-lg text-gray-300 leading-relaxed">
{props.message}
</p>
<p className="mt-2 text-sm text-gray-400 leading-relaxed">
Join the Whatsapp Group for further updates immediately.
<p className="mt-2 text-md text-gray-400 leading-relaxed">
Join the WhatsApp Group for further updates immediately.
</p>
</div>

<div className="flex mx-auto items-center mt-3">
<a href={props.joinLink}>
<button className="flex-1 px-4 py-2 mx-auto bg-green-500 hover:bg-green-600 text-white text-sm rounded-md">
Join the Whatsapp Group!
<div className="flex mx-auto items-center mt-6">
<a
href={props.joinLink}
className="w-full"
>
<button className="w-full px-6 py-3 bg-green-500 hover:bg-green-600 text-white text-lg font-semibold rounded-full transition duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-opacity-50">
Join WhatsApp Group!
</button>
</a>
</div>
</div>
</div>
);
);

}
Loading