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
446 changes: 247 additions & 199 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
"@auth/prisma-adapter": "^2.7.4",
"@octokit/rest": "^21.0.2",
"@prisma/client": "^5.11.0",
"@sendgrid/mail": "^8.1.4",
"@splinetool/react-spline": "^4.0.0",
"@types/react-syntax-highlighter": "^15.5.13",
"axios": "^1.6.2",
"bcrypt": "^5.0.1",
"bowser": "^2.11.0",
"date-fns": "^3.4.0",
"firebase": "^11.1.0",
"firebase": "^11.2.0",
"gray-matter": "^4.0.3",
"gsap": "^3.12.5",
"lerp": "^1.0.3",
Expand Down Expand Up @@ -59,4 +60,4 @@
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
}
4 changes: 4 additions & 0 deletions public/images/icons/mail.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
86 changes: 86 additions & 0 deletions src/app/api/newsletter/send/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { NextResponse } from "next/server";
import sgMail from "@sendgrid/mail";
import { initializeApp } from "firebase/app";
import { getFirestore, collection, getDocs, query, where } from "firebase/firestore";

// Initialize SendGrid
sgMail.setApiKey(process.env.SENDGRID_API_KEY || "");

// Firebase configuration
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export async function POST(request: Request) {
try {
// Verify admin authentication (implement your own auth logic)
// This is just a basic example using an admin key
const { adminKey, subject, content } = await request.json();

if (adminKey !== process.env.NEWSLETTER_ADMIN_KEY) {
return NextResponse.json(
{ success: false, message: "Unauthorized" },
{ status: 401 }
);
}

// Get all active subscribers
const subscribersRef = collection(db, "newsletter_subscribers");
const q = query(subscribersRef, where("status", "==", "active"));
const querySnapshot = await getDocs(q);

const subscribers = querySnapshot.docs.map(doc => doc.data());

// Prepare newsletter email
const msg = {
from: process.env.SENDGRID_FROM_EMAIL || "[email protected]",
subject: subject,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
${content}
<hr style="margin: 20px 0;">
<p style="font-size: 12px; color: #666;">
You're receiving this email because you subscribed to DevRhylme Foundation's newsletter.
<br>
<a href="{unsubscribe_url}" style="color: #4A6CF7;">Unsubscribe</a>
</p>
</div>
`,
};

// Send newsletter to all subscribers
const emailPromises = subscribers.map(subscriber => {
return sgMail.send({
...msg,
to: subscriber.email,
dynamicTemplateData: {
name: subscriber.name,
unsubscribe_url: `${process.env.NEXT_PUBLIC_BASE_URL}/api/newsletter/unsubscribe?email=${subscriber.email}`,
},
});
});

await Promise.all(emailPromises);

return NextResponse.json({
success: true,
message: `Newsletter sent to ${subscribers.length} subscribers`
});

} catch (error) {
console.error("Error sending newsletter:", error);
return NextResponse.json(
{ success: false, message: "Failed to send newsletter" },
{ status: 500 }
);
}
}
54 changes: 54 additions & 0 deletions src/app/api/newsletter/subscribe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { NextResponse } from "next/server";
import sgMail from "@sendgrid/mail";

// Initialize SendGrid with API key
sgMail.setApiKey(process.env.SENDGRID_API_KEY || "");

export async function POST(request: Request) {
try {
const { name, email } = await request.json();

// Prepare welcome email
const msg = {
to: email,
from: process.env.SENDGRID_FROM_EMAIL || "[email protected]",
subject: "Welcome to DevRhylme Foundation Newsletter!",
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #4A6CF7;">Welcome to DevRhylme Foundation!</h2>
<p>Hello ${name},</p>
<p>Thank you for subscribing to our newsletter! We're excited to have you join our community.</p>
<p>You'll now receive updates about:</p>
<ul>
<li>Latest innovations in AI and Blockchain</li>
<li>Upcoming events and workshops</li>
<li>Community initiatives and projects</li>
<li>Open source contributions and opportunities</li>
</ul>
<p>Stay connected with us:</p>
<div style="margin: 20px 0;">
<a href="https://github.com/DEVRhylme-Foundation" style="color: #4A6CF7; margin-right: 15px;">GitHub</a>
<a href="https://discord.gg/xjwZzGKDVR" style="color: #4A6CF7; margin-right: 15px;">Discord</a>
<a href="https://www.youtube.com/@DevRhylme1" style="color: #4A6CF7;">YouTube</a>
</div>
<p>Best regards,<br>The DevRhylme Foundation Team</p>
</div>
`,
};

// Send welcome email
await sgMail.send(msg);

return NextResponse.json({
success: true,
message: "Welcome email sent successfully"
});

} catch (error) {
console.error("Error sending welcome email:", error);
return NextResponse.json(
{ success: false, message: "Failed to send welcome email" },
{ status: 500 }
);
}
}
103 changes: 103 additions & 0 deletions src/app/api/newsletter/unsubscribe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { NextResponse } from "next/server";
import { initializeApp } from "firebase/app";
import { getFirestore, collection, query, where, getDocs, updateDoc } from "firebase/firestore";

// Firebase configuration
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const email = searchParams.get("email");

if (!email) {
return NextResponse.json(
{ success: false, message: "Email is required" },
{ status: 400 }
);
}

// Find subscriber
const subscribersRef = collection(db, "newsletter_subscribers");
const q = query(subscribersRef, where("email", "==", email));
const querySnapshot = await getDocs(q);

if (querySnapshot.empty) {
return NextResponse.json(
{ success: false, message: "Subscriber not found" },
{ status: 404 }
);
}

// Update subscriber status to unsubscribed
const subscriberDoc = querySnapshot.docs[0];
await updateDoc(subscriberDoc.ref, {
status: "unsubscribed",
unsubscribedAt: new Date().toISOString(),
});

// Return unsubscribe confirmation page
return new Response(
`
<!DOCTYPE html>
<html>
<head>
<title>Unsubscribed - DevRhylme Foundation</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
max-width: 600px;
margin: 40px auto;
padding: 20px;
text-align: center;
color: #333;
}
h1 { color: #4A6CF7; }
.message { margin: 20px 0; }
.footer { margin-top: 40px; font-size: 14px; color: #666; }
a { color: #4A6CF7; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>Successfully Unsubscribed</h1>
<div class="message">
<p>You have been successfully unsubscribed from the DevRhylme Foundation newsletter.</p>
<p>We're sorry to see you go! If you change your mind, you can always subscribe again from our website.</p>
</div>
<div class="footer">
<p>
<a href="/">Return to Homepage</a>
</p>
</div>
</body>
</html>
`,
{
headers: {
"Content-Type": "text/html",
},
}
);

} catch (error) {
console.error("Error unsubscribing:", error);
return NextResponse.json(
{ success: false, message: "Failed to unsubscribe" },
{ status: 500 }
);
}
}
Loading
Loading