const crypto = require('crypto'); const { User, Setting } = require('../../models'); const { getConfig } = require('../../config/config'); const { logError, logInfo } = require('../../services/logService'); const { sendEmail } = require('../../services/emailService'); const { validateEmail, validatePassword } = require('../users/userService'); const isRegistrationEnabled = async () => { const setting = await Setting.findOne({ where: { key: 'registration_enabled' }, }); // Default to false if setting doesn't exist return setting ? setting.value === 'true' : false; }; const setRegistrationEnabled = async (enabled) => { await Setting.upsert({ key: 'registration_enabled', value: String(enabled), }); logInfo(`Registration ${enabled ? 'enabled' : 'disabled'} by admin`); }; const generateVerificationToken = () => { return crypto.randomBytes(32).toString('hex'); }; const getTokenExpirationDate = () => { const config = getConfig(); const hours = config.registrationConfig.tokenExpiryHours; const expirationDate = new Date(); expirationDate.setHours(expirationDate.getHours() + hours); return expirationDate; }; const createUnverifiedUser = async (email, password, transaction = null) => { if (!validateEmail(email)) { throw new Error('Invalid email format'); } if (!validatePassword(password)) { throw new Error('Password must be at least 6 characters long'); } const existingUser = await User.findOne({ where: { email }, transaction, }); if (existingUser) { throw new Error('Email already registered'); } const verificationToken = generateVerificationToken(); const tokenExpiresAt = getTokenExpirationDate(); const user = await User.create( { email, password, email_verified: false, email_verification_token: verificationToken, email_verification_token_expires_at: tokenExpiresAt, }, { transaction } ); return { user, verificationToken, tokenExpiresAt, }; }; const isVerificationTokenValid = (token, expiresAt) => { if (!token || !expiresAt) { return false; } const now = new Date(); const expiration = new Date(expiresAt); return now <= expiration; }; const verifyUserEmail = async (token) => { if (!token) { throw new Error('Verification token is required'); } const user = await User.findOne({ where: { email_verification_token: token }, }); if (!user) { throw new Error('Invalid verification token'); } if (user.email_verified) { throw new Error('Email already verified'); } if ( !isVerificationTokenValid( token, user.email_verification_token_expires_at ) ) { throw new Error('Verification token has expired'); } user.email_verified = true; user.email_verification_token = null; user.email_verification_token_expires_at = null; await user.save(); return user; }; const sendVerificationEmail = async (user, verificationToken) => { const config = getConfig(); const { isEmailEnabled } = require('../../services/emailService'); if (!isEmailEnabled()) { logInfo( `Email service is disabled. Verification email for ${user.email} not sent. User must be verified manually.` ); return { success: false, reason: 'Email service is disabled' }; } const verificationUrl = `${config.backendUrl}/api/verify-email?token=${verificationToken}`; const tokenExpiryHours = config.registrationConfig.tokenExpiryHours; const subject = 'Welcome to Tududi - Verify your email'; const text = `Welcome to Tududi! Thank you for registering. To complete your registration and start using Tududi, please verify your email address by clicking the link below: ${verificationUrl} This verification link will expire in ${tokenExpiryHours} hours. After verification, you'll be able to: - Create and organize tasks with ease - Manage projects and areas - Set up recurring tasks - Track your productivity If you didn't create an account with Tududi, you can safely ignore this email. Best regards, The Tududi Team`; const html = `

Welcome to Tududi!

Thank you for registering. To complete your registration and start using Tududi, please verify your email address by clicking the button below:

Verify Email Address

Or copy and paste this link into your browser:

${verificationUrl}

This verification link will expire in ${tokenExpiryHours} hours.

After verification, you'll be able to:

If you didn't create an account with Tududi, you can safely ignore this email.

Best regards,
The Tududi Team

`; const result = await sendEmail({ to: user.email, subject, text, html, }); if (result.success) { logInfo(`Verification email sent to ${user.email}`); return result; } else { logError( new Error(result.reason), `Failed to send verification email to ${user.email}` ); return result; } }; const cleanupExpiredTokens = async () => { const now = new Date(); try { const result = await User.update( { email_verification_token: null, email_verification_token_expires_at: null, }, { where: { email_verified: false, email_verification_token_expires_at: { [require('sequelize').Op.lt]: now, }, }, } ); if (result[0] > 0) { logInfo(`Cleaned up ${result[0]} expired verification tokens`); } return result[0]; } catch (error) { logError(error, 'Failed to cleanup expired tokens'); return 0; } }; module.exports = { isRegistrationEnabled, setRegistrationEnabled, generateVerificationToken, createUnverifiedUser, verifyUserEmail, sendVerificationEmail, isVerificationTokenValid, cleanupExpiredTokens, };