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
47 changes: 47 additions & 0 deletions DevoteApp/app/api/auth/check-verification-status/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NextResponse } from "next/server";
import connectToDb from "../../../../lib/mongodb/mongodb";
import User from "../../../../models/user";
import { VerificationUtils } from "../../../../lib/verification";

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

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

await connectToDb();

const user = await User.findOne({ email }).exec();
if (!user) {
return NextResponse.json(
{ message: "User not found" },
{ status: 404 }
);
}

return NextResponse.json(
{
isEmailVerified: user.isEmailVerified,
hasVerificationCode: !!user.emailVerificationCode,
codeExpired: user.emailVerificationExpires ?
VerificationUtils.isCodeExpired(user.emailVerificationExpires) : true,
attemptsUsed: user.emailVerificationAttempts,
canRequestNew: VerificationUtils.canSendEmail(user.lastVerificationEmailSent)
},
{ status: 200 }
);

} catch (error: any) {
console.error("Error checking verification status:", error);
return NextResponse.json(
{ message: "Internal server error" },
{ status: 500 }
);
}
}
79 changes: 79 additions & 0 deletions DevoteApp/app/api/auth/send-verification/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { NextResponse } from "next/server";
import connectToDb from "../../../../lib/mongodb/mongodb";
import User from "../../../../models/user";
import { EmailService } from "../../../../lib/email";
import { VerificationUtils } from "../../../../lib/verification";

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

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

await connectToDb();

const user = await User.findOne({ email }).exec();
if (!user) {
return NextResponse.json(
{ message: "User not found" },
{ status: 404 }
);
}

if (user.isEmailVerified) {
return NextResponse.json(
{ message: "Email is already verified" },
{ status: 400 }
);
}

// Check rate limiting
if (!VerificationUtils.canSendEmail(user.lastVerificationEmailSent)) {
return NextResponse.json(
{ message: "Please wait before requesting another verification email" },
{ status: 429 }
);
}

// Generate new verification code
const verificationCode = VerificationUtils.generateVerificationCode();
const hashedCode = VerificationUtils.hashVerificationCode(verificationCode);
const expirationDate = VerificationUtils.getExpirationDate();

// Update user with new verification code
user.emailVerificationCode = hashedCode;
user.emailVerificationExpires = expirationDate;
user.emailVerificationAttempts = 0; // Reset attempts
user.lastVerificationEmailSent = new Date();
await user.save();

// Send verification email
const emailService = new EmailService();
const { subject, text, html } = VerificationUtils.generateVerificationEmail(
verificationCode,
user.name
);

await emailService.sendMail(user.email, subject, text, html);

return NextResponse.json(
{
message: "Verification email sent successfully",
expiresAt: expirationDate
},
{ status: 200 }
);

} catch (error: any) {
console.error("Error sending verification email:", error);
return NextResponse.json(
{ message: "Internal server error" },
{ status: 500 }
);
}
}
100 changes: 100 additions & 0 deletions DevoteApp/app/api/auth/verify-email/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// app/api/auth/verify-email/route.ts
import { NextResponse } from "next/server";
import connectToDb from "../../../../lib/mongodb/mongodb";
import User from "../../../../models/user";
import { VerificationUtils } from "../../../../lib/verification";

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

if (!email || !code) {
return NextResponse.json(
{ message: "Email and verification code are required" },
{ status: 400 }
);
}

await connectToDb();

const user = await User.findOne({ email }).exec();
if (!user) {
return NextResponse.json(
{ message: "User not found" },
{ status: 404 }
);
}

if (user.isEmailVerified) {
return NextResponse.json(
{ message: "Email is already verified" },
{ status: 400 }
);
}

// Check if user has exceeded attempts
if (VerificationUtils.hasExceededAttempts(user.emailVerificationAttempts)) {
return NextResponse.json(
{
message: "Too many failed attempts. Please request a new verification code.",
requireNewCode: true
},
{ status: 429 }
);
}

// Check if code has expired
if (VerificationUtils.isCodeExpired(user.emailVerificationExpires)) {
return NextResponse.json(
{
message: "Verification code has expired. Please request a new one.",
expired: true
},
{ status: 400 }
);
}

// Increment attempts
user.emailVerificationAttempts += 1;
await user.save();

// Verify the code
if (!VerificationUtils.verifyCode(code, user.emailVerificationCode)) {
const attemptsLeft = 5 - user.emailVerificationAttempts;
return NextResponse.json(
{
message: `Invalid verification code. ${attemptsLeft} attempts remaining.`,
attemptsLeft
},
{ status: 400 }
);
}

// Success! Mark email as verified
user.isEmailVerified = true;
user.emailVerificationCode = ""; // Clear the code
user.emailVerificationExpires = new Date(); // Set to past date
user.emailVerificationAttempts = 0;
await user.save();

return NextResponse.json(
{
message: "Email verified successfully",
user: {
id: user._id,
name: user.name,
email: user.email,
isEmailVerified: user.isEmailVerified
}
},
{ status: 200 }
);

} catch (error: any) {
console.error("Error verifying email:", error);
return NextResponse.json(
{ message: "Internal server error" },
{ status: 500 }
);
}
}
57 changes: 38 additions & 19 deletions DevoteApp/app/api/users/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import connectToDb from "../../../lib/mongodb/mongodb";
import Citizen from "../../../models/citizen";
import User from "../../../models/user";
import crypto from "crypto";
// KYC imports removed - no longer needed
// import { createKyc, getSdkLink } from "../../../lib/kyc";
import { EmailService } from "../../../lib/email";
import { VerificationUtils } from "../../../lib/verification";
import {
generatePrivateKeyEncrypted,
getFutureWalletAdressFromPrivateKey,
Expand Down Expand Up @@ -43,45 +42,65 @@ export async function POST(req: Request) {
);
}

// Check for existing email
const existingEmail = await User.findOne({ email }).exec();
if (existingEmail) {
return NextResponse.json(
{ message: "User already exists with provided email" },
{ status: 400 }
);
}

const name = `${citizen.firstName} ${citizen.lastName}`;
const privateKey = generatePrivateKeyEncrypted("1234");
const walletAddress = getFutureWalletAdressFromPrivateKey(
privateKey,
"1234"
);

// Generate verification code for email verification
const verificationCode = VerificationUtils.generateVerificationCode();
const hashedCode = VerificationUtils.hashVerificationCode(verificationCode);
const expirationDate = VerificationUtils.getExpirationDate();

const newUser = new User({
walletId: walletAddress,
name,
email,
hashIne: hashedIne,
// KYC fields removed - lines 30-36, 48, 61-63, 69, 71 deleted
secretKey: privateKey,
// Email verification fields
isEmailVerified: false,
emailVerificationCode: hashedCode,
emailVerificationExpires: expirationDate,
emailVerificationAttempts: 0,
lastVerificationEmailSent: new Date(),
});

await newUser.save();

// KYC creation logic removed - no longer needed
// const kycId = await createKyc(String(newUser._id), newUser.email);
// newUser.kycId = kycId;
// await newUser.save();

// Send verification email instead of account creation email
const emailService = new EmailService();
const subject = "Account Created Successfully";

// Updated email message as requested (lines 75-84)
const frontendUrl = process.env.FRONTEND_URL || "https://devote-nine.vercel.app/";
const verificationUrl = `${frontendUrl}/verification-submitted?id=${newUser._id}`;

const text = `A user account has been created for you. Please click the following link to set your password: ${verificationUrl}`;
const html = `<p>A user account has been created for you. Please click the following link to set your password:</p>
<p><a href="${verificationUrl}">${verificationUrl}</a></p>
<p><strong>⚠️ NOTE: Before testing this flow, make sure to send Sepolia ETH to the generated wallet before clicking the link in the email.</strong></p>`;
const { subject, text, html } = VerificationUtils.generateVerificationEmail(
verificationCode,
newUser.name
);

await emailService.sendMail(newUser.email, subject, text, html);

return NextResponse.json(
{ message: "User created successfully", user: newUser },
{
message: "User created successfully. Verification email sent.",
user: {
id: newUser._id,
name: newUser.name,
email: newUser.email,
walletId: newUser.walletId,
isEmailVerified: newUser.isEmailVerified
},
verificationSent: true,
expiresAt: expirationDate
},
{ status: 201 }
);
} catch (error: any) {
Expand Down
Loading