- Added RenewalCronService to handle automatic subscription renewals and reactivations. - Introduced listPausedAutoRenew method in AbonemmentRepository to fetch paused subscriptions eligible for reactivation. - Created test script for renewal cron job to simulate subscription renewal scenarios. - Updated MailService to send renewal confirmation and payment reminder emails. - Enhanced EmailVerificationService to auto-grant 'can_subscribe' permission upon email verification. - Modified createAdminUser script to allow different admin email configurations. - Added node-cron dependency for scheduling tasks.
114 lines
4.7 KiB
JavaScript
114 lines
4.7 KiB
JavaScript
const EmailVerificationRepository = require('../../repositories/email/EmailVerificationRepository');
|
|
const MailService = require('./MailService');
|
|
const { logger } = require('../../middleware/logger');
|
|
|
|
class EmailVerificationService {
|
|
static async sendVerificationEmail(user, unitOfWork) {
|
|
logger.info('EmailVerificationService.sendVerificationEmail:start', { userId: user.id, email: user.email });
|
|
const emailVerificationRepo = new EmailVerificationRepository(unitOfWork);
|
|
|
|
try {
|
|
// Check if already verified
|
|
const status = await unitOfWork.connection.query(
|
|
'SELECT email_verified FROM user_status WHERE user_id = ?', [user.id]
|
|
);
|
|
if (status[0][0] && status[0][0].email_verified) {
|
|
logger.warn('EmailVerificationService.sendVerificationEmail:already_verified', { userId: user.id });
|
|
throw new Error('Email already verified');
|
|
}
|
|
|
|
// Generate 6-digit code and expiry
|
|
const code = Math.floor(100000 + Math.random() * 900000).toString();
|
|
const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
|
|
|
|
// Upsert code in DB
|
|
await emailVerificationRepo.upsertCode(user.id, code, expiresAt);
|
|
logger.info('EmailVerificationService.sendVerificationEmail:code_upserted', { userId: user.id });
|
|
|
|
// Send email
|
|
await MailService.sendVerificationCodeEmail({
|
|
email: user.email,
|
|
code,
|
|
expiresAt
|
|
});
|
|
logger.info('EmailVerificationService.sendVerificationEmail:email_sent', { userId: user.id, email: user.email });
|
|
|
|
logger.info('EmailVerificationService.sendVerificationEmail:success', { userId: user.id });
|
|
return true;
|
|
} catch (error) {
|
|
logger.error('EmailVerificationService.sendVerificationEmail:error', { userId: user.id, error: error.message });
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
static async verifyCode(userId, code, unitOfWork) {
|
|
logger.info('EmailVerificationService.verifyCode:start', { userId, code });
|
|
const emailVerificationRepo = new EmailVerificationRepository(unitOfWork);
|
|
|
|
try {
|
|
// Get latest, unexpired, unverified code
|
|
const record = await emailVerificationRepo.getByUserId(userId);
|
|
if (
|
|
!record ||
|
|
record.verified_at ||
|
|
new Date(record.expires_at) < new Date() ||
|
|
record.verification_code !== code
|
|
) {
|
|
logger.warn('EmailVerificationService.verifyCode:invalid_or_expired', { userId, code });
|
|
// Optionally increment attempts
|
|
if (record) {
|
|
await emailVerificationRepo.incrementAttempts(record.id);
|
|
}
|
|
return { success: false, error: 'Invalid or expired code' };
|
|
}
|
|
|
|
// Mark as verified
|
|
await emailVerificationRepo.setVerified(record.id);
|
|
logger.info('EmailVerificationService.verifyCode:code_verified', { userId });
|
|
|
|
// Update user_status
|
|
await unitOfWork.connection.query(
|
|
`UPDATE user_status SET email_verified = 1, email_verified_at = NOW() WHERE user_id = ?`,
|
|
[userId]
|
|
);
|
|
logger.info('EmailVerificationService.verifyCode:user_status_updated', { userId });
|
|
|
|
// Check if all steps are complete and set status to 'pending' if so
|
|
const UserStatusService = require('../status/UserStatusService');
|
|
await UserStatusService.checkAndSetPendingIfComplete(userId, unitOfWork);
|
|
logger.info('EmailVerificationService.verifyCode:pending_check_complete', { userId });
|
|
|
|
// Auto-grant can_subscribe permission if user doesn't already have it
|
|
try {
|
|
const [permRows] = await unitOfWork.connection.query(
|
|
`SELECT id FROM permissions WHERE name = 'can_subscribe' AND is_active = TRUE LIMIT 1`
|
|
);
|
|
if (permRows.length > 0) {
|
|
const permId = permRows[0].id;
|
|
const [existing] = await unitOfWork.connection.query(
|
|
`SELECT 1 FROM user_permissions WHERE user_id = ? AND permission_id = ? LIMIT 1`,
|
|
[userId, permId]
|
|
);
|
|
if (existing.length === 0) {
|
|
await unitOfWork.connection.query(
|
|
`INSERT INTO user_permissions (user_id, permission_id) VALUES (?, ?)`,
|
|
[userId, permId]
|
|
);
|
|
logger.info('EmailVerificationService.verifyCode:can_subscribe_granted', { userId });
|
|
}
|
|
}
|
|
} catch (permError) {
|
|
logger.error('EmailVerificationService.verifyCode:permission_grant_error', { userId, error: permError.message });
|
|
}
|
|
|
|
logger.info('EmailVerificationService.verifyCode:success', { userId });
|
|
return { success: true };
|
|
} catch (error) {
|
|
logger.error('EmailVerificationService.verifyCode:error', { userId, error: error.message });
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = EmailVerificationService;
|