monacousa-portal/server/api/auth/send-verification-email.pos...

138 lines
4.2 KiB
TypeScript

export default defineEventHandler(async (event) => {
try {
const body = await readBody(event);
const { email } = body;
if (!email || typeof email !== 'string') {
throw createError({
statusCode: 400,
statusMessage: 'Email is required'
});
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid email format'
});
}
console.log('[send-verification-email] Processing request for email:', email);
// Check if user exists in Keycloak
const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin');
const keycloak = createKeycloakAdminClient();
let existingUsers;
try {
existingUsers = await keycloak.findUserByEmail(email.toLowerCase().trim());
} catch (error: any) {
console.error('[send-verification-email] Failed to search users:', error.message);
throw createError({
statusCode: 500,
statusMessage: 'Failed to verify account status'
});
}
if (!existingUsers || existingUsers.length === 0) {
throw createError({
statusCode: 404,
statusMessage: 'No account found with this email address'
});
}
const user = existingUsers[0];
// Check if user is already verified
if (user.emailVerified) {
throw createError({
statusCode: 400,
statusMessage: 'This email address is already verified'
});
}
// Rate limiting: check if we recently sent an email to this address
const rateLimitKey = `verification_email_${email.toLowerCase()}`;
// Simple in-memory rate limiting (in production, use Redis)
const globalCache = globalThis as any;
if (!globalCache.verificationEmailCache) {
globalCache.verificationEmailCache = new Map();
}
const lastSent = globalCache.verificationEmailCache.get(rateLimitKey);
const cooldownPeriod = 2 * 60 * 1000; // 2 minutes
if (lastSent && Date.now() - lastSent < cooldownPeriod) {
throw createError({
statusCode: 429,
statusMessage: 'Please wait a few minutes before requesting another verification email'
});
}
// Generate verification token
const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens');
const verificationToken = await generateEmailVerificationToken(user.id, email);
// Get configuration
const config = useRuntimeConfig();
const verificationLink = `${config.public.domain}/api/auth/verify-email?token=${verificationToken}`;
// Send verification email
const { getEmailService } = await import('~/server/utils/email');
const emailService = getEmailService();
try {
await emailService.sendWelcomeEmail(email, {
firstName: user.firstName || '',
lastName: user.lastName || '',
verificationLink,
memberId: user.id
});
console.log('[send-verification-email] Successfully sent verification email to:', email);
// Update rate limiting cache
globalCache.verificationEmailCache.set(rateLimitKey, Date.now());
// Clean up old rate limit entries periodically
if (Math.random() < 0.1) { // 10% chance
const now = Date.now();
for (const [key, timestamp] of globalCache.verificationEmailCache.entries()) {
if (now - timestamp > cooldownPeriod * 2) {
globalCache.verificationEmailCache.delete(key);
}
}
}
return {
success: true,
message: 'Verification email sent successfully'
};
} catch (emailError: any) {
console.error('[send-verification-email] Failed to send email:', emailError.message);
throw createError({
statusCode: 500,
statusMessage: 'Failed to send verification email. Please try again later.'
});
}
} catch (error: any) {
console.error('[send-verification-email] Request failed:', error.message);
// Re-throw HTTP errors
if (error.statusCode) {
throw error;
}
// Handle unexpected errors
throw createError({
statusCode: 500,
statusMessage: 'An unexpected error occurred. Please try again.'
});
}
});