Skip to content
This repository was archived by the owner on Nov 12, 2024. It is now read-only.

feat: added google-recaptca verification on form #5

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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
15 changes: 10 additions & 5 deletions app/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,28 @@ export async function getToken(
): Promise<TMessageResult> {
const address = formData.get("address");
const choice = formData.get("choice");
const captchaToken = formData.get("captchaToken");

try {
const parsedAddress = addressSchema.parse(address);
const parsedChoice: EChoices = choiceSchema.parse(choice);

const res = await fetch(
`https://faucet.aelf.dev/api/${
`https://faucet-staging.aelf.dev/api/${
{
[EChoices.ELF]: "claim",
[EChoices.TOKEN]: "claim-seed",
[EChoices.NFT]: "claim-nft-seed",
}[parsedChoice]
}?walletAddress=${parsedAddress}`,
{ method: "POST" }
}?walletAddress=${parsedAddress}&recaptchaToken=${captchaToken}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Platform: "FaucetUI", // Add the custom header here
},
}
);
const json = await res.json();

const result = messageResultSchema.parse(json);

return result;
Expand Down
49 changes: 42 additions & 7 deletions app/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useFormState, useFormStatus } from "react-dom";
import { getToken } from "./actions";
import { TMessageResult } from "./validation";
import { useState } from "react";
import { useState, useRef } from "react";
import { EChoices } from "./types";
import {
Select,
Expand All @@ -14,23 +14,32 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import ReCAPTCHA from "react-google-recaptcha"; // Import reCAPTCHA

const GOOGLE_CAPTCH_SITEKEY = "6LcovGQqAAAAAM7XGg4LijV20JucjYa1sum0oU_k";

const initialState: TMessageResult = {
message: "",
isSuccess: true,
code: 0,
};

function SubmitButton({ choice }: { choice: EChoices }) {
function SubmitButton({
choice,
isCaptchaVerified,
}: {
choice: EChoices;
isCaptchaVerified: boolean;
}) {
const { pending } = useFormStatus();

return (
<button
className={`bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded ${
pending ? "opacity-50 cursor-not-allowed" : ""
pending || !isCaptchaVerified ? "opacity-50 cursor-not-allowed" : ""
}`}
type="submit"
aria-disabled={pending}
aria-disabled={pending || !isCaptchaVerified}
>
Get {choice === EChoices.ELF ? "Tokens" : "Seed"}
</button>
Expand All @@ -40,15 +49,28 @@ function SubmitButton({ choice }: { choice: EChoices }) {
function Form() {
const [state, formAction] = useFormState(getToken, initialState);
const [choice, setChoice] = useState<EChoices>(EChoices.ELF);
const [isCaptchaVerified, setIsCaptchaVerified] = useState(false);
const [captchaToken, setCaptchaToken] = useState<string>("");
const recaptchaRef = useRef<ReCAPTCHA>(null);

const isSeed = choice !== EChoices.ELF;

// Handle reCAPTCHA verification
const onReCAPTCHAChange = (token: string | null) => {
if (token) {
setIsCaptchaVerified(true);
setCaptchaToken(token);
} else {
setIsCaptchaVerified(false);
}
};

return (
<div className="mx-auto md:w-[800px]">
<h1 className="text-4xl font-bold text-gray-800">
AElf Testnet {isSeed ? "Seed" : "Token"} Faucet
</h1>
<div className="sm:rounded-md p-6 border border-gray-300 my-4 md:h-[500px] flex flex-col">
<div className="sm:rounded-md p-6 border border-gray-300 my-4 md:min-h-[500px] flex flex-col">
<form action={formAction}>
<label className="block mb-6">
<span className="text-gray-700">Your aelf address</span>
Expand All @@ -62,7 +84,11 @@ function Form() {

<Select
value={choice}
onValueChange={(e) => setChoice(e as EChoices)}
onValueChange={(e) => {
setChoice(e as EChoices);
recaptchaRef && recaptchaRef.current?.reset();
setIsCaptchaVerified(false);
}}
>
<SelectTrigger>
<SelectValue placeholder="Select type" />
Expand All @@ -83,8 +109,17 @@ function Form() {
<div className="mb-4"></div>

<input type="hidden" name="choice" value={choice} />
<input type="hidden" name="captchaToken" value={captchaToken} />

{/* Add Google reCAPTCHA */}
<ReCAPTCHA
ref={recaptchaRef}
sitekey={GOOGLE_CAPTCH_SITEKEY} // Replace with your reCAPTCHA site key
onChange={onReCAPTCHAChange}
className="mb-3"
/>

<SubmitButton choice={choice} />
<SubmitButton choice={choice} isCaptchaVerified={isCaptchaVerified} />
{state.message.length > 0 ? (
<p
aria-live="polite"
Expand Down
51 changes: 48 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"next": "14.2.2",
"react": "^18",
"react-dom": "^18",
"react-google-recaptcha": "^3.1.0",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.23.3"
Expand All @@ -26,6 +27,7 @@
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/react-google-recaptcha": "^2.1.9",
"eslint": "^8",
"eslint-config-next": "14.2.2",
"postcss": "^8",
Expand Down