Skip to content

Commit

Permalink
Merge pull request #76 from celestiaorg/hotfix/adding-recaptcha-back-…
Browse files Browse the repository at this point in the history
…to-newsletter

Adding recaptcha, validation, error handling back to the form and api…
  • Loading branch information
sysrex authored Jan 29, 2025
2 parents 5c90937 + 2ee67c1 commit b64ae01
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 300 deletions.
155 changes: 0 additions & 155 deletions src/app/api/newsletter/route.js

This file was deleted.

56 changes: 54 additions & 2 deletions src/app/api/subscribe/route.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
import { NextResponse } from "next/server";

// Add validation check for required env vars
const requiredEnvVars = ["MAILCHIMP_API_KEY", "MAILCHIMP_LIST_ID", "MAILCHIMP_SERVER_PREFIX", "RECAPTCHA_SECRET_KEY"];
for (const envVar of requiredEnvVars) {
if (!process.env[envVar]) {
console.error(`Missing required environment variable: ${envVar}`);
throw new Error(`Missing required environment variable: ${envVar}`);
}
}

async function verifyRecaptcha(token) {
try {
const response = await fetch("https://www.google.com/recaptcha/api/siteverify", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: `secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`,
});

const data = await response.json();
if (!data.success) {
console.error("reCAPTCHA verification failed:", data["error-codes"]);
}
return data.success;
} catch (error) {
console.error("reCAPTCHA verification failed:", error);
return false;
}
}

export async function POST(req) {
try {
const { email } = await req.json();
const { email, token } = await req.json();

// Verify reCAPTCHA first
if (!token || !(await verifyRecaptcha(token))) {
return NextResponse.json({ error: "Invalid reCAPTCHA" }, { status: 400 });
}

if (!email || !email.includes("@")) {
if (!isValidEmail(email)) {
return NextResponse.json({ error: "Invalid email address" }, { status: 400 });
}

Expand Down Expand Up @@ -38,17 +73,34 @@ export async function POST(req) {
}
}

if (response.status === 429) {
return NextResponse.json({ error: "Too many requests" }, { status: 429 });
}

const data = await response.json();

if (!response.ok) {
console.error("Mailchimp API Error:", data);
return NextResponse.json({ error: data.detail || "Failed to subscribe" }, { status: response.status });
}

return NextResponse.json({ success: true });
} catch (error) {
console.error("API Error:", error);
if (error.name === "TimeoutError") {
return NextResponse.json({ error: "Request timed out" }, { status: 408 });
}
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}

function isValidEmail(email) {
const emailRegex =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
return (
typeof email === "string" &&
email.length <= 320 && // Max email length
email.length >= 3 && // Min reasonable length
emailRegex.test(email)
);
}
6 changes: 2 additions & 4 deletions src/components/Footer/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import Link from "@/macros/Link/Link";
import Icon from "@/macros/Icons/Icon";
import ArrowLongSVG from "@/macros/SVGs/ArrowLongSVG";
import { isInternalLink } from "@/utils/isInternalLink";
// import Newsletter from "@/components/Newsletter/Newsletter";
import NewsletterSimple from "@/components/NewsletterSimple/NewsletterSimple";
import Newsletter from "@/components/Newsletter/Newsletter";

const Footer = () => {
const columns = footerData();
Expand All @@ -31,8 +30,7 @@ const Footer = () => {
<Heading size='lg' className={`mb-12`}>
Build whatever with Celestia underneath
</Heading>
{/* <Newsletter /> */}
<NewsletterSimple />
<Newsletter />
</div>
<div className={`flex flex-wrap lg:flex-nowrap w-full lg:1/2 lg:gap-6 lg:justify-end ml-auto mr-0`}>
{columns.map((column, index) => {
Expand Down
13 changes: 4 additions & 9 deletions src/components/Newsletter/Newsletter.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ const Newsletter = () => {

const handleSubmit = async (e) => {
e.preventDefault();

console.log("Submitting with token:", token);

if (isSubmitting) return;
if (!email) {
setStatus("Error");
Expand All @@ -54,15 +51,12 @@ const Newsletter = () => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

const response = await fetch("/api/newsletter", {
const response = await fetch("/api/subscribe", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
token,
}),
body: JSON.stringify({ email, token }),
signal: controller.signal,
});

Expand Down Expand Up @@ -137,7 +131,6 @@ const Newsletter = () => {
type={"submit"}
disabled={isSubmitting}
onClick={(e) => {
console.log("Button clicked"); // Debug log
if (!isSubmitting) {
handleSubmit(e);
}
Expand All @@ -146,11 +139,13 @@ const Newsletter = () => {
{isSubmitting ? "Subscribing..." : "Subscribe"}
</PrimaryButton>
</Row>

{siteKey && (
<Row className='mt-3'>
<ReCAPTCHA sitekey={siteKey} ref={reCaptchaRef} onChange={onReCAPTCHAChange} />
</Row>
)}

{captchaError && (
<Row className='px-2 mt-2'>
<Body size={"sm"} className={"text-red-error"}>
Expand Down
Loading

0 comments on commit b64ae01

Please sign in to comment.