-
-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bc5a638
commit 6924ed5
Showing
16 changed files
with
864 additions
and
49 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,106 @@ | ||
import {db} from "@/lib/drizzle"; | ||
import {otps, users} from "@/lib/schema"; | ||
import {signupSchema} from "@/lib/zod"; | ||
import {hash} from "bcryptjs"; | ||
import {NextRequest, NextResponse} from "next/server"; | ||
import {z} from "zod"; | ||
|
||
import otpEmailTemplate from "@/lib/templates/otp-template"; | ||
import mailer from "@/lib/mailer"; | ||
|
||
|
||
// `[email protected]` email is for development only | ||
import {z} from "zod"; // Zod is a validation library for handling structured data validation | ||
import {hash} from "bcryptjs"; // bcryptjs is used for securely hashing passwords | ||
import {db} from "@/lib/drizzle"; // This imports the database handler from Drizzle ORM | ||
import mailer from "@/lib/mailer"; // This imports the mailer function for sending emails | ||
import {signupSchema} from "@/lib/zod"; // A Zod schema for validating incoming signup data | ||
import {otps, users} from "@/lib/schema"; // Imports the OTP and users tables from the database schema | ||
import {NextRequest, NextResponse} from "next/server"; // These are Next.js server functions for handling HTTP requests and responses | ||
import otpEmailTemplate from "@/lib/templates/otp-template"; // A template for the OTP email | ||
|
||
// Define the sender email address for OTP verification emails. | ||
// This is used in the development environment as a fallback if the environment variable is not set. | ||
const senderEmail = process.env.SENDER_EMAIL || "[email protected]"; | ||
|
||
|
||
// POST request handler for user registration | ||
export async function POST(req: NextRequest) { | ||
try { | ||
// Parse the incoming request JSON to extract the user data | ||
const payload = await req.json(); | ||
|
||
// Validate the request data using the predefined Zod schema | ||
// This checks that the data conforms to the expected types and structure | ||
const {email, name, password, username, alerts} = signupSchema.parse(payload) | ||
|
||
// Basic validation to ensure required fields are provided | ||
if (!email || !name || !password || !username) { | ||
return NextResponse.json( | ||
{error: "name, username, email and password are required."}, | ||
{status: 400} | ||
); | ||
} | ||
|
||
// Check if a user with the same username already exists in the database | ||
const userWithUsername = await db.query.users.findFirst({ | ||
where: (users, {eq}) => eq(users.username, username), | ||
}); | ||
|
||
// If the username already exists, return an error | ||
if (userWithUsername) throw new Error("User with username already exists."); | ||
|
||
// Check if a user with the same email already exists | ||
const userWithEmail = await db.query.users.findFirst({ | ||
where: (users, {eq}) => eq(users.email, email), | ||
}); | ||
|
||
// If the email already exists, return an error | ||
if (userWithEmail) throw new Error("User with email already exists."); | ||
|
||
// If no duplicates, hash the password for secure storage | ||
const hashedPassword = await hash(password, 10); | ||
|
||
// Insert the new user data into the 'users' table in the database | ||
await db.insert(users).values({ | ||
username, | ||
email, | ||
name, | ||
password: hashedPassword, | ||
alerts, | ||
username | ||
alerts // 'alerts' is a user setting, likely indicating whether the user wants notifications | ||
}); | ||
|
||
// Generate a 6-digit OTP (One Time Password) for email verification | ||
const otp = Math.floor(100000 + Math.random() * 900000).toString(); | ||
|
||
const otp = Math.floor(100000 + Math.random() * 900000).toString(); // 6-digit OTP | ||
// Set expiration time (e.g., 10 minutes from now) | ||
// Set an expiration time for the OTP (10 minutes from now) | ||
const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes in the future | ||
|
||
// Fetch the user from the database by email to associate the OTP with the correct user | ||
const user = await db.query.users.findFirst({ | ||
where: (users, {eq}) => eq(users.email, email), | ||
}) | ||
}); | ||
|
||
// Insert the OTP and its expiration time into the 'otps' table | ||
await db.insert(otps).values({ | ||
expiresAt, | ||
otp, | ||
userId: user!.id, | ||
createdAt: new Date(), | ||
userId: user!.id, // Use the user's ID from the fetched record | ||
createdAt: new Date(), // Set the current time as the creation time of the OTP | ||
}); | ||
|
||
|
||
// Send an email to the user with the OTP for email verification | ||
await mailer.sendMail({ | ||
from: `Uttarakhand Culture <${senderEmail}>`, | ||
to: [email], | ||
subject: 'Verify you email with OTP', | ||
html: otpEmailTemplate(name, otp), | ||
}) | ||
|
||
from: `Uttarakhand Culture <${senderEmail}>`, // From address (e.g., a sender email) | ||
to: [email], // Recipient email | ||
subject: 'Verify you email with OTP', // Email subject | ||
html: otpEmailTemplate(name, otp), // The email body generated using the OTP template | ||
}); | ||
|
||
// Return a success response indicating the user has been registered | ||
return NextResponse.json({ | ||
message: `Registered. Now login!`, | ||
}); | ||
|
||
} catch (error: any) { | ||
// Handle errors that occur during the registration process | ||
|
||
// If the error is a Zod validation error, return the validation error details | ||
if (error instanceof z.ZodError) { | ||
return NextResponse.json({error: error.errors}, {status: 500}); | ||
} | ||
|
||
// Log unexpected errors to the console for debugging purposes | ||
console.log("[REGISTER_ERROR]: ", error); | ||
|
||
// Return a generic error message | ||
return NextResponse.json({error: error.message}, {status: 500}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { NextResponse } from 'next/server'; | ||
import mailer from '@/lib/mailer'; | ||
|
||
export async function POST(request: Request) { | ||
try { | ||
const { name, email, reason, message } = await request.json(); | ||
|
||
if (!name || !email || !reason || !message) { | ||
return NextResponse.json({ error: 'All fields are required.' }, { status: 400 }); | ||
} | ||
|
||
// Send the email using Nodemailer | ||
await mailer.sendMail({ | ||
from: process.env.SENDER_EMAIL, | ||
to: process.env.CONTACT_FORM_RECEIVER_EMAIL, | ||
subject: `Contact Form: ${reason}`, | ||
html: ` | ||
<div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;"> | ||
<h2 style="color: #0070f3;">You Have a New Contact Form Submission</h2> | ||
<p><strong>Name:</strong> ${name}</p> | ||
<p><strong>Email:</strong> ${email}</p> | ||
<p><strong>Reason:</strong> ${reason}</p> | ||
<p><strong>Message:</strong></p> | ||
<blockquote style="background: #f9f9f9; padding: 15px; border-left: 5px solid #0070f3; margin: 10px 0;"> | ||
${message} | ||
</blockquote> | ||
<hr style="border: none; border-top: 1px solid #ddd; margin: 20px 0;"> | ||
<p style="font-size: 12px; color: #888;">This is an automated email. Please do not reply to this email.</p> | ||
</div> | ||
`, | ||
}); | ||
|
||
return NextResponse.json({ message: 'Email sent successfully.' }, { status: 200 }); | ||
} catch (error) { | ||
console.error('Error sending email:', error); | ||
return NextResponse.json({ error: 'Failed to send email.' }, { status: 500 }); | ||
} | ||
} | ||
|
||
// Handle other HTTP methods | ||
export async function GET() { | ||
return NextResponse.json({ error: 'Method Not Allowed' }, { status: 405 }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* Container styles */ | ||
.container { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 85vh; | ||
box-sizing: border-box; | ||
padding: 20px; | ||
} | ||
|
||
/* Card styles */ | ||
.card { | ||
background: #ffffff; | ||
border-radius: 12px; | ||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | ||
width: 100%; | ||
max-width: 500px; | ||
min-width: 300px; | ||
padding: 20px; | ||
text-align: center; | ||
overflow: hidden; | ||
transition: transform 0.3s ease-in-out; | ||
} | ||
|
||
.card:hover { | ||
transform: translateY(-10px); | ||
} | ||
|
||
/* Title styles */ | ||
.title { | ||
font-size: 26px; | ||
font-weight: 600; | ||
color: #333333; | ||
margin-bottom: 10px; | ||
} | ||
|
||
/* Subtitle styles */ | ||
.subtitle { | ||
font-size: 16px; | ||
color: #666666; | ||
line-height: 1.6; | ||
} | ||
|
||
.lastsubtitle{ | ||
font-size: 16px; | ||
color: #666666; | ||
line-height: 1.6; | ||
margin-bottom: 20px; | ||
} | ||
|
||
/* Form styles */ | ||
.form { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 20px; | ||
} | ||
|
||
/* Input group styles */ | ||
.inputGroup { | ||
margin-bottom: 15px; | ||
} | ||
|
||
/* Label styles */ | ||
.label { | ||
font-size: 15px; | ||
font-weight: 500; | ||
color: #333333; | ||
text-align: left; | ||
margin-bottom: 8px; | ||
display: block; | ||
} | ||
|
||
/* Input field styles */ | ||
.input, | ||
.textarea { | ||
width: 100%; | ||
padding: 12px; | ||
font-size: 15px; | ||
border: 1px solid #cccccc; | ||
border-radius: 6px; | ||
outline: none; | ||
transition: border-color 0.3s ease; | ||
} | ||
|
||
.input:focus, | ||
.textarea:focus { | ||
border-color: #0070f3; | ||
} | ||
|
||
/* Textarea styles */ | ||
.textarea { | ||
height: auto; | ||
min-height: 120px; | ||
resize: vertical; | ||
transition: height 0.2s ease; | ||
|
||
} | ||
|
||
/* Button styles */ | ||
.button { | ||
padding: 12px 24px; | ||
background-color: #333333; | ||
color: #ffffff; | ||
border: none; | ||
border-radius: 6px; | ||
font-size: 16px; | ||
cursor: pointer; | ||
transition: background-color 0.3s ease; | ||
} | ||
|
||
.button:hover { | ||
background-color: #444444; | ||
} | ||
|
||
/* Status message styles */ | ||
.status { | ||
margin-top: 20px; | ||
font-size: 15px; | ||
color: #0070f3; | ||
text-align: center; | ||
} | ||
|
||
/* Responsive styles */ | ||
@media (max-width: 768px) { | ||
.container { | ||
padding: 10px; | ||
} | ||
|
||
.card { | ||
padding: 15px; | ||
} | ||
} |
Oops, something went wrong.